本次討論單向鏈表的排序。本質上講,鏈表的排序與數組的排序在算法上有很多相通的地方,但是由於單向鏈表只能向后訪問的特殊性,那些要求隨機訪問的排序算法在鏈表的排序上並不能施展手腳,所以只能采用相鄰比較的排序方法:冒泡法,而且只能從前向后冒泡。鏈表的另一個問題是由於長度不是已知的,所以終止條件只能通過節點是否為空進行判斷,而每次的循環次數也是如此。下面是兩種排序方法,一種求出長度再排序,另一種直接進行排序。
另一種排序要求是倒序。當然,我們可以修改count1(),使其不分青紅皂白總是交換相鄰節點,只是復雜度較高。另一種優美簡練的算法就是下面的reverse方法。代碼中添加了注釋,應該能看明白。核心思想就是化整為零,每次把即將要倒序的節點指向已倒序完成的節點序列,然后指針右移,直至結束。
// 計算鏈表長度
public int count() {
Node2<T> temp = head;
int count = 0;
while (temp != null) {
count++;
temp = temp.next;
}
return count;
}
// 用於兩節點數據交換
private void swap(Node2<T> Node21, Node2<T> Node2) {
T temp = Node21.key;
Node21.key = Node2.key;
Node2.key = temp;
}
// 由鏈表長度控制循環判斷條件
public void sort1() {
int n = count();
if (n > 1) {// 至少兩個節點才能排序;
for (int i = 0; i < n; i++) {
Node2<T> temp = head;
for (int j = 0; j < n - i - 1; j++) {// 相鄰比較,前者小則互換值。(升序排列)
if (((Person) temp.key).compareTo((Person) temp.next.key) == 1)
swap(temp, temp.next);
temp = temp.next;// 指針右移
}
}
}
}
// 此處維護兩個指針,index記錄外層循環的起始位置,每次右移;temp和sort1()方法里的temp指針作用一致,表示內層循環。
public void sort2() {
Node2<T> index = head;
while (index != null) {
Node2<T> temp = index;
while (temp != null && temp.next != null) {
if (((Person) temp.key).compareTo((Person) temp.next.key) == -1)
swap(temp, temp.next);
temp = temp.next;
}
// 注意此處,由於鏈表只能向右訪問,導致最值只能冒向右側。我們的外層循環指針之所以也是右移,是因為下面對頭節點和尾節點進行了互換,使得最 值相當於冒向左側。
swap(index, temp);
index = index.next;
}
}
//此處為鏈表的反轉操作,邏輯存在一定難度,但已是我見過的精煉優美的代碼
public ListNode reverse(ListNode head) {
ListNode result = null;聲明一個鏈表
while(head != null){
ListNode temp = head.next;(1)//因下一步要修改head.next,(1)將右移指針保存起來,(4)最后再給head;
head.next = result;(2)//(2)就是把下一個等待倒序的節點(head)指向已經倒序完成的result節點,這樣又有一個節點完成了倒序;
result = head;(3)//(3)把結果重新指向最新的倒序節點序列;
head = temp;(4)//指針右移;
}
return result;
}