連結串列和歸併排序(Merge Sort)
阿新 • • 發佈:2019-01-06
歸併排序適合於對連結串列進行原址排序,即只改變指標的連線方式,不交換連結串列結點的內容。
歸併排序的基本思想是分治法:先把一個連結串列分割成只有一個節點的連結串列,然後按照一定順序、自底向上合併相鄰的兩個連結串列。
只要保證各種大小的子連結串列是有序的,那麼最後返回的連結串列就一定是有序的.
歸併排序分為分割和合並兩個子過程。分割是用遞迴的方法,把連結串列對半分割成兩個子連結串列;合併是在遞迴返回(回朔)的時候,把兩個有序連結串列合併成一個有序連結串列。
(注意:只有一個節點的連結串列一定是有序的)
這裡sort過程就是分割過程;merge過程就是合併且排序的過程
說到分割連結串列,那麼問題來了:連結串列不是隨機訪問的,我怎麼知道分割點在哪裡?一個寶貴的經驗就是:維護兩個指標,一快一慢。快指標每次後移兩個單位,慢指標每次只移動一個單位。當快指標移動到tail或者最後一個有效節點時,慢指標就指向了中間的節點。
sort過程:
Node* sort (Node* beg) { if(beg==tail || beg->next==tail) return beg; Node* a = beg; Node* b = beg->next; while(b!=tail && b->next != tail) { a = a->next; b = b->next->next; } b = a->next; //the beginning of right part a->next = tail; //the end of left part return merge(sort(beg), sort(b)); }
把連結串列分割之後就要合併。merge操作傳入的引數是兩個有序連結串列,返回的是合併後的有序的連結串列。兩個有序連結串列簡單拼接之後不一定是有序的,需要對每一個元素重排。這個重排的過程是從兩個連結串列各自最小(最大)元素開始,誰小(大)就把誰放到新的連結串列裡。
Node* LinkedList<T>::merge(Node* a, Node* b) { Node dummy = Node(); Node* head = &dummy; // temp是正在合併的表的節點 Node* temp = head; while(a!=tail && b!=tail) //逐個比較連結串列a和連結串列b的每個元素 { if(a->data <= b->data) { // 如果a比b小, 那麼當前結點的後繼就是a temp->next = a; // 把當前節點移向後繼 temp = a; // a後移 a = a->next; } else { temp->next = b; temp = b; b = b->next; } // 如果原表a已經排完,那麼新表後面就放b的剩餘元素 // 否則仍然以a為標準和b進行比較 temp->next = (a==tail) ? b : a; } return head->next; }