最近與人瞎聊,聊到各大廠的面試題,其中有一個就是用java實現單鏈表反轉。閑來無事,決定就這個問題進行一番嘗試。
1.準備鏈表
準備一個由DataNode組成的單向鏈表,DataNode如下:
public class DataNode {
private int data;
private DataNode next;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public DataNode getNext() {
return next;
}
public void setNext(DataNode next) {
this.next = next;
}
public DataNode(int data) {
this.data = data;
}
}
構造鏈表
public class DataChain {
private DataNode head;
public DataChain(int size) {
DataNode head = new DataNode(0);
DataNode cur = head;
for (int i = 1; i < size; i++) {
DataNode tmp = new DataNode(i);
cur.setNext(tmp);
cur = tmp;
}
this.head = head;
}
public DataNode getHead() {
return head;
}
public void setHead(DataNode head) {
this.head = head;
}
public static void printChain(DataNode head) {
StringBuilder sb = new StringBuilder();
DataNode cur = head;
sb.append(cur.getData());
while (null != cur.getNext()) {
sb.append(" -> ");
sb.append(cur.getNext().getData());
cur = cur.getNext();
}
System.out.println(sb.toString());
}
public static void main(String... strings) {
DataChain chain = new DataChain(10);
printChain(chain.getHead());
}
}
運行main方法,即構造了一個包含10個node節點的單鏈表。
#運行結果
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
2.通過遞歸實現單鏈表反轉
考慮到代碼的簡潔性,首先考慮的是通過遞歸實現。
/**
* 遞歸實現 當棧深度大于12000 則會出現StakOverflowError
*
* @param head
* @return
*/
public static DataNode reverse1(DataNode head) {
if (null == head || null == head.getNext())
return head;
DataNode revHead = reverse1(head.getNext());
head.getNext().setNext(head);
head.setNext(null);
return revHead;
}
以上即是遞歸實現的源碼,但是需要考慮的問題是遞歸都在java棧中進行,需要考慮jdk支持的棧的深度。在jdk1.8.0_91版本中,當上述鏈表長度大于12000則會出現StackOverFlowError錯誤。說明對于該版本jdk棧的深度不能大于12000。
3.通過遍歷實現
最通用的實現方式就是遍歷。
/**
* 遍歷實現 通用實現方法
*
* @param head
* @return
*/
public static DataNode reverse2(DataNode head) {
if (null == head || null == head.getNext())
return head;
DataNode pre = head;
DataNode cur = head.getNext();
while (null != cur.getNext()) {
DataNode tmp = cur.getNext();
cur.setNext(pre);
pre = cur;
cur = tmp;
}
cur.setNext(pre);
head.setNext(null);
return cur;
}
4.借助stack實現
考慮到stack具有先進后出這一特性,因此可以借助于stack數據結構來實現單向鏈表的反轉。
/**
* 方法3 利用其他數據結構 stack
* @param head
* @return
*/
public static DataNode reverse3(DataNode head) {
Stack<DataNode> stack = new Stack<DataNode>();
for (DataNode node = head; null != node; node = node.getNext()) {
stack.add(node);
}
DataNode reHead = stack.pop();
DataNode cur = reHead;
while(!stack.isEmpty()){
cur.setNext(stack.pop());
cur = cur.getNext();
cur.setNext(null);
}
return reHead;
}
上述實現方法在于操作簡單,對于算法并不精通的同學可以嘗試。缺點在于需要通過其他數據結構實現,效率會降低,至于效率會降低到什么程度,后面舉例說明。
5.三種實現方式效率分析
public static void main(String... strings) {
int size = 10;
DataChain chain1 = new DataChain(size);
printChain(chain1.getHead());
long reverse1_start = System.currentTimeMillis();
DataNode reNode1 = reverse1(chain1.getHead());
long reverse1_cost = System.currentTimeMillis() - reverse1_start;
printChain(reNode1);
System.out.println("reverse1 cost time is ["+reverse1_cost+"]ms");
DataChain chain2 = new DataChain(size);
printChain(chain2.getHead());
long reverse2_start = System.currentTimeMillis();
DataNode reNode2 = reverse2(chain2.getHead());
long reverse2_cost = System.currentTimeMillis() - reverse2_start;
printChain(reNode2);
System.out.println("reverse2 cost time is ["+reverse2_cost+"]ms");
DataChain chain3 = new DataChain(size);
printChain(chain3.getHead());
long reverse3_start = System.currentTimeMillis();
DataNode reNode3 = reverse3(chain3.getHead());
long reverse3_cost = System.currentTimeMillis() - reverse3_start;
printChain(reNode3);
System.out.println("reverse3 cost time is ["+reverse3_cost+"]ms");
}
執行結果:
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0
reverse1 cost time is [0]ms
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0
reverse2 cost time is [0]ms
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0
reverse3 cost time is [1]ms
在上述代碼基礎上,去掉打印輸出,將size改為10000,結果如下:
reverse1 cost time is [1]ms
reverse2 cost time is [0]ms
reverse3 cost time is [6]ms
可以看出reverse2 明顯優于其他兩種實現方法。考慮到reverse1最多只支持12000,因此將size改為100000時,再觀察reverse2和reverse3之間的執行結果:
reverse2 cost time is [6]ms
reverse3 cost time is [25]ms
因此可以看出,最好的方法是采用遍歷的方式進行反轉。