Java資料結構和演算法(一)線性結構之單鏈表
阿新 • • 發佈:2018-12-13
Java資料結構和演算法(一)線性結構之單鏈表
prev current next
-------------- -------------- --------------
| value | next | -> | value | next | -> | value | next |
-------------- -------------- --------------
單鏈表的結構如上:最後一個節點的 next=null。下面看一下程式碼。
(1) 連結串列的基本操作
public class Node<E> { private E value; private Node next; public Node(E value) { this.value = value; } // 追加到最後一個元素 public Node append(Node node) { Node tail = tail(); tail.next(node); return this; } // 刪除指定的節點 public void remove(Node node) { Node prev = prev(node); if (prev != null) { prev.next = node.next; } } // 節點總數 public int size() { int size = 1; Node current = this; while (current.next != null) { size++; current = current.next; } return size; } // 查詢指定節點的上一個節點 public Node prev(Node node) { Node prev = this; while (prev != null) { if (prev.next == node) { return prev; } prev = prev.next; } return null; } // 查詢尾節點,單鏈表 tail.next=null public Node tail() { Node tail = this; while (tail.next != null) { tail = tail.next; } return tail; } // 設定當前節點的下一個節點 public void next(Node next) { // 設定該節點的後繼節點 next.next = this.next; // 將該節點設定為當前節點的前驅節點 this.next = next; } public Node next() { return next; } public E getValue() { return value; } public void setValue(E value) { this.value = value; } }
(2) 取出中間節點
偶數節點取中間兩個節點的前一個節點,奇數節點取正中間的節點
public Node mid() { Node stepOneNode = this; Node stepTwoNode = this; while (stepTwoNode != null) { stepTwoNode = stepTwoNode.next; if (stepTwoNode != null) { stepTwoNode = stepTwoNode.next; if (stepTwoNode != null) { stepOneNode = stepOneNode.next; } } } return stepOneNode; }
(3) 連結串列反轉
public Node reverse() {
Node prev = null;
Node next = null;
Node current = this;
while (current != null) {
next = current.next;
current.next = prev;
prev = current;
current = next;
}
return prev;
}
測試一把:
public void test1() {
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
n1.append(n2).append(n3);
Assert.assertEquals(3, n1.next().next().getValue());
Assert.assertEquals(3, n1.tail().getValue());
Assert.assertEquals(2, n1.prev(n3).getValue());
Assert.assertEquals(3, n1.size());
n1.remove(n2);
Assert.assertEquals(3, n1.next().getValue());
Assert.assertEquals(1, n1.mid().getValue());
n1.next(n2);
Assert.assertEquals(3, n1.next().next().getValue());
Assert.assertEquals(2, n1.mid().getValue());
Node reverse = n1.reverse();
Assert.assertEquals(3, reverse.getValue());
Assert.assertEquals(2, reverse.next().getValue());
Assert.assertEquals(1, reverse.next().next().getValue());
}
(4) 有序連結串列的合併
兩個有序連結串列合併後還是有序的,程式碼如下:
// 有序連結串列合併,兩個連結串列均升序排列,最終的結果也升序排列
public static Node merge(Node<Integer> node1, Node<Integer> node2) {
if (node1 == null || node2 == null) {
return node1 == null ? node2 : node1;
}
Node<Integer> head = node1.value < node2.value ? node1 : node2;
Node<Integer> cur1 = head == node1 ? node1 : node2; // 小
Node<Integer> cur2 = head == node1 ? node2 : node1; // 大
Node prev = null; // curl1 的前驅節點,小
while (cur1 != null && cur2 != null) {
if (cur1.value < cur2.value) {
prev = cur1;
cur1 = cur1.next;
} else {
// 將 curl2 插入到 prev 和 curl1 之間
Node tmp = cur2.next;
cur2.next = cur1;
prev.next = cur2;
prev = cur2;
cur2 = tmp;
}
}
prev.next = cur1 == null ? cur2 : cur1;
return head;
}
// 有序連結串列合併,兩個連結串列均升序排列,最終的結果也升序排列
public static Node mergeRecurse(Node<Integer> node1, Node<Integer> node2) {
if (node1 == null || node2 == null) {
return node1 != null ? node1 : node2;
}
Node head = null;
if (node1.value > node2.value) {
head = node2;
head.next = mergeRecurse(node1, node2.next);
} else {
head = node1;
head.next = mergeRecurse(node1.next, node2);
}
return head;
}
測試:
public void mergeTest() {
Node n1 = new Node(1);
// 省略...
Node n6 = new Node(6);
n1.append(n3).append(n5);
n2.append(n4).append(n6);
Node merge1 = Node.merge(n1, n2);
//Node merge1 = Node.mergeRecurse(n1, n2);
Assert.assertEquals(6, merge1.size());
Assert.assertEquals(2, merge1.next().getValue());
}
每天用心記錄一點點。內容也許不重要,但習慣很重要!