高效連結串列排序-歸併演算法
排序演算法應該是最基礎的了,快速、歸併、選擇、堆排序等等
對於陣列而言可以隨機訪問,那麼對於連結串列呢,比如快排,兩個指標分別網後往前走,而沒有前驅指標的單向連結串列,無法完成這樣的操作,當然了可以採用快慢指標的方式,在提交leetcode的時候,發現快排是會超時的。
那麼對於連結串列而言,可以採取一種怎麼樣的高效排序演算法呢
歸併排序
分而治之,分別拍好前後兩個部分,然後合併兩個有序連結串列,在合適不過,並且由於連結串列自帶屬性,合併連結串列還無需0(n)額外的空間,因此歸併排序成為連結串列排序的首選。
我一直認為歸併排序是挺好理解的,也比較簡單,但是看了看網上的部落格,為什麼總感覺要麼是放一堆程式碼,要麼放一堆很長很長的程式碼,不如自己寫個簡單點的,完全按照陣列的方式。
歸併排序有幾個子問題,連結串列分割,有序連結串列合併
連結串列分割
將一個連結串列分為為兩個長度相等的連結串列(單數時會多一個)。
這個問題就相當於找到連結串列的中點,很簡單的使用快慢指標,快指標走到尾節點,慢指標剛好走到一半。
第一個連結串列的頭為head,第二個連結串列的頭為慢指標的next指標。先拍後面,在拍前面。
此時需要注意,連結串列要分成兩份,從中間斷開,因此需要將慢節點的next指標設定為NULL很關鍵!!!。
ListNode* sort_list(ListNode *head)
{
if (head == NULL||head-> next==NULL)//如果沒有元素或者只有一個元素,那麼就直接退出
return head;
ListNode *l = NULL,*r=NULL;
l = head;
r = head;
while (r->next && r->next->next)//找到中間節點
{
r = r->next->next;
l = l->next;
}
if (r == head)//如果此時只有兩個節點,那麼中間節點就是頭節點,l=r=head,無限迴圈,因此特殊判斷
{
r = r->next;
l->next = NULL;
return merge_list(r, l);
}
r=sort_list(l->next);//排序後半部分
l->next = NULL;//注意,一定要把前面從中間節點切斷
l=sort_list(head);//排序前面一部分
ListNode* temp = merge_list(l, r);//合併兩個有序連結串列
return temp;
}
有序連結串列合併
類似於合併有序陣列,不過不需要額外的空間了。
比如 連結串列1 1->3->7->9
連結串列2 2->4->10->29
由於需要返回一個新的頭部,而頭部必然為連結串列1的頭部或連結串列2的頭部,因此不需要另外新建了。
程式碼如下
ListNode * merge_list(ListNode *head1, ListNode *head2)
{
ListNode *newhead = NULL ,*p=NULL;
if (head1 == NULL)
return head2;
if (head2 == NULL)
return head1;
if (head1->val <= head2->val)
{
newhead = head1;
head1 = head1->next;
}
else
{
newhead = head2;
head2 = head2->next;
}
p = newhead;
while (head1 != NULL && head2 != NULL)
{
if (head1->val <= head2->val)
{
p->next = head1;
head1 = head1->next;
p = p->next;
}
else
{
p->next = head2;
head2 = head2->next;
p = p->next;
}
}
while (head1 != NULL)
{
p->next = head1;
head1 = head1->next;
p = p->next;
}
while (head2 != NULL)
{
p->next = head2;
head2 = head2->next;
p = p->next;
}
p->next = NULL;
return newhead;
}
然後就是一個遞迴呼叫的過程
完整測試程式碼
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution {
public:
ListNode * merge_list(ListNode *head1, ListNode *head2)
{
ListNode *newhead = NULL ,*p=NULL;
if (head1 == NULL)
return head2;
if (head2 == NULL)
return head1;
if (head1->val <= head2->val)
{
newhead = head1;
head1 = head1->next;
}
else
{
newhead = head2;
head2 = head2->next;
}
p = newhead;
while (head1 != NULL && head2 != NULL)
{
if (head1->val <= head2->val)
{
p->next = head1;
head1 = head1->next;
p = p->next;
}
else
{
p->next = head2;
head2 = head2->next;
p = p->next;
}
}
while (head1 != NULL)
{
p->next = head1;
head1 = head1->next;
p = p->next;
}
while (head2 != NULL)
{
p->next = head2;
head2 = head2->next;
p = p->next;
}
p->next = NULL;
return newhead;
}
ListNode* sort_list(ListNode *head)
{
if (head == NULL||head->next==NULL)//如果沒有元素或者只有一個元素,那麼就直接退出
return head;
ListNode *l = NULL,*r=NULL;
l = head;
r = head;
while (r->next && r->next->next)//找到中間節點
{
r = r->next->next;
l = l->next;
}
if (r == head)//如果此時只有兩個節點,那麼中間節點就是頭節點,l=r=head,無限迴圈,因此特殊判斷
{
r = r->next;
l->next = NULL;
return merge_list(r, l);
}
r=sort_list(l->next);//排序後半部分
l->next = NULL;//注意,一定要把前面從中間節點切斷
l=sort_list(head);//排序前面一部分
ListNode* temp = merge_list(l, r);//合併兩個有序連結串列
return temp;
}
void printt_list(ListNode *head)
{
while (head)
{
cout << head->val << " ";
head = head->next;
}
}
void insert(ListNode *head,int val)
{
while (head->next)
{
head = head->next;
}
head->next = new ListNode(val);
}
};
int main()
{
Solution test;
ListNode * head = new ListNode(1);
test.insert(head, 42);
test.insert(head, 13);
test.insert(head, 6);
test.insert(head, 234);
test.insert(head, 19);
test.insert(head, 52);
test.insert(head, 41);
test.insert(head, 322);
test.insert(head, 234);
head=test.sort_list(head);
test.printt_list(head);
}
相關推薦
高效連結串列排序-歸併演算法
排序演算法應該是最基礎的了,快速、歸併、選擇、堆排序等等 對於陣列而言可以隨機訪問,那麼對於連結串列呢,比如快排,兩個指標分別網後往前走,而沒有前驅指標的單向連結串列,無法完成這樣的操作,當然了可以採用快慢指標的方式,在提交leetcode的時候,發現快排是會
連結串列排序演算法java實現(連結串列的快速排序、插入排序、歸併排序)
難易程度:★★ 重要性:★★★ 連結串列的排序相對陣列的排序更為複雜些,也是考察求職者是否真正理解了排序演算法(而不是“死記硬背”) 連結串列的插入排序 public class LinkedInsertSort { static cla
連結串列排序(冒泡、選擇、插入、快排、歸併、希爾、堆排序)
參考http://www.cnblogs.com/TenosDoIt/p/3666585.html 插入排序(演算法中是直接交換節點,時間複雜度O(n^2),空間複雜度O(1)) 1 class Solution { 2 public: 3 ListNode *ins
幾種連結串列排序演算法的JAVA實現
插入排序 首先解釋一下一個概念叫做:前繼節點,即我們要的節點的前一個節點。我當時寫這個演算法的時候一直在猶豫的一點在於我怎麼儲存下個節點的狀態,因為在單向連結串列中的必須要儲存下個節點的狀態才能對當前節點進行操作,而另外一點是我應該怎麼樣利用當前的值和前一個值比較。這是我在做
連結串列和歸併排序(Merge Sort)
歸併排序適合於對連結串列進行原址排序,即只改變指標的連線方式,不交換連結串列結點的內容。 歸併排序的基本思想是分治法:先把一個連結串列分割成只有一個節點的連結串列,然後按照一定順序、自底向上合併相鄰的兩個連結串列。 只要保證各種大小的子連結串列是有序的,那麼最後返回的連結
連結串列的歸併排序,無需輔助外存
rt, 最近的面試題,面的時候敲的血崩,特此寫下部落格以作留念 如有錯誤,歡迎指正 class Node{ int val; Node next; } import java.util.Random; class Node {
演算法愛好者——連結串列排序 ? 待解決
請寫一個函式實現在 O(n log n) 時間複雜度和常數級的空間複雜度下給連結串列排序。 格式: 輸入第一行輸入一個連結串列,最後輸入按升序排列後的連結串列。 樣例輸入 1 -> 3 -> 2 -> null 樣例輸出 1 -&g
python實現陣列和連結串列的歸併排序
歸併排序是一種穩定的排序,採用分而治之策略,可以用於順序儲存結構,也易於在連結串列上實現。其原理如下圖: 演算法時間複雜度為 O(nlogn),空間複雜度為 O(n)。1 在陣列上實現d
【郝斌資料結構自學筆記】26_通過連結串列排序演算法的演示再次詳細討論到底什麼是演算法以及到底什麼是泛型【重點】
26_通過連結串列排序演算法的演示再次詳細討論到底什麼是演算法以及到底什麼是泛型【重點】 演算法: 狹義的演算法是與資料的儲存方式密切相關 廣義的演算法是與資料的儲存方式無關 泛型: 利用某種技術達到的效果就是:不同的儲存方式,執行的操作是一樣的 #include<
C++ 連結串列的歸併排序
void disp_nodeList(ListNode *p) { while (p!=NULL) { disp(p->val); p = p->n
常用連結串列排序演算法
========================== 功能:選擇排序(由小到大) 返回:指向連結串列表頭的指標 ========================== */ /* 選擇排序的基本思想就是反覆從還未排好序的那些節點中, 選出鍵值(就是用它排序的欄位,我們取學號num為鍵值)最小的節點,
【多次過】Lintcode 98. 連結串列排序
在O(n log n)時間複雜度和常數級的空間複雜度下給連結串列排序。 樣例 給出 1->3->2->null,給它排序變成 1->2->3->null。 挑戰 分別用歸併排序和快速排序做一遍。
劍指offer--關於連結串列操作的演算法
題1:輸入一個連結串列,輸出該連結串列中倒數第k個結點。 思路1: 因為我們拿到的是第一個結點,不知道最後一個結點在哪。但是我們可以發現,從第一個結點走到倒數第K個結點的長度和正著第k個結點到最後一個結點的長度一樣。所以我們可以用兩個指標指向第一個結點,讓第一個指證先走k-1步,然後兩個指證
連結串列排序(LeetCode 148)
Sort a linked list in O(n log n) time using constant space complexity. Example 1:Input: 4->2->1->3 Output: 1->2->3->4 Example 2:
1870 Problem B C語言-連結串列排序
問題 B: C語言-連結串列排序 時間限制: 1 Sec 記憶體限制: 128 MB 提交: 86 解決: 71 [提交][狀態][討論版][命題人:外部匯入] 題目描述 已有a、b兩個連結串列,每個連結
leetcode148 連結串列排序
在 O(n log n) 時間複雜度和常數級空間複雜度下,對連結串列進行排序。 使用歸併排序 public ListNode sortList(ListNode head) { if(head == null || head.next == null)
連結串列檢測環演算法,找到環中的出口節點
如何判斷一個連結串列有環 方法是使用快慢指標,通過一個slow指標(每次都指向下一個),一個quick指標(每次都指向下面兩個) 因為假設有環的話,quick會追上slow指標 找到環出口就是通過slow指標指向頭節點,quick指標指向之前環的交叉點,然後一直以不同速度遍歷直到相遇
《程式設計基礎》實驗題目2 c檔案讀取(反序列化?) 連結串列排序
題目: 每個學生的資訊卡片包括學號、姓名和成績三項。定義儲存學生資訊的單向連結串列的結點型別;編寫函 數,由檔案依次讀入 n(n≥0)個學生的資訊,建立一個用於管理學生資訊的單向連結串列;編寫函式,對 該連結串列進行整理,保證該單向連結串列的結點順序滿足學號從小到大的順序。 演算法的設計與
雙向連結串列的構造(演算法4習題1.3.31)
原題:實現一個巢狀類DoubleNode用來構造雙向連結串列,其中每個節點都含有一個指向前驅元素的引用和一個指向後續元素的引用(如果不存在則為NULL)。為以下任務實現若干靜態方法:在表頭插入結點、在表尾插入結點、在表頭刪除結點、在表尾刪除結點、在指定結點之前插入新結點、在指定結點之後插入新結點、刪除指定
有序連結串列的歸併(資料結構)
連結串列的歸併有好多種做法,連結串列的難度並不是在思想上,而是在程式碼的實現上,指標的來回轉換很容易將人弄混亂,一會不知道指標到哪了,一個好好的指標可能在不知不覺中就變成了一個野指標,沒有物件也沒有記憶體。。。 以SDUT的一個題目為例來解釋一下連結串列的歸併: 資料結構實驗之連結串列四:有