合并两个排序好的链表/链表去重/链式快排
2013-10-01 02:39
489 查看
合并两个已经排好序的链表:
递归方式:如果其中一个链表为空,则直接返回第二个链表,如果第二个链表为空,则返回第一个链表。如果两个链表都不为空,则判断两个链表的表头大小,小值当作合并链表的头结点MergeHead,然后,后面的问题也是新的子问题,递归调用即可,具体可以看代码体会。
非递归方式:如果其中一个链表为空,则直接返回第二个链表,如果第二个链表为空,则返回第一个链表。如果两个链表都不为空,则判断两个链表的表头大小,小值当作合并链表的头结点MergeHead,同时需要另外三个指针指示位置,分别是MergeTail,head1,head2,往后迭代地比较head1和head2,MergeTail总是指向min(head1.key,head2.key),同时MergeHead与head1,head2是同步前移的,直至head1或者head2有一个为NULL,则MergeTail直接指向另一个链表的head即可。具体可以看代码体会。
从一个未排序的链表中移除重复的项,增加一个条件:如果不允许使用临时的缓存,你如何解决这个问题?
思路:
1.如果可以使用额外空间,那就空间换时间,新建一个额外的数组保存之前出现过的项,下一次再出现同样的项,就删除一个.这种办法需要O(n)的空间和O(n)的时间.
2.如果不可以使用额外的空间,那可以考虑用蛮力法,用两个指针,每次扫描到一个结点,就用另一个指针扫描它后面的结点,删除重复项,时间效率是O(n^2).
3.如果可以先将链表排好序,那么删除重复结点的时间复杂度可以降到O(n),加上排序的复杂度,是O(nlgn+n).
以下是三种思路的实现:
思路1:
思路2:
思路3:我们可以用改良版的链式快排对链表进行快速排序.然后,只需要比较相较相邻的结点value是否相同,则选择删除与否即可.
链式快排:(从线性快排稍做改进可以得到)
递归方式:如果其中一个链表为空,则直接返回第二个链表,如果第二个链表为空,则返回第一个链表。如果两个链表都不为空,则判断两个链表的表头大小,小值当作合并链表的头结点MergeHead,然后,后面的问题也是新的子问题,递归调用即可,具体可以看代码体会。
非递归方式:如果其中一个链表为空,则直接返回第二个链表,如果第二个链表为空,则返回第一个链表。如果两个链表都不为空,则判断两个链表的表头大小,小值当作合并链表的头结点MergeHead,同时需要另外三个指针指示位置,分别是MergeTail,head1,head2,往后迭代地比较head1和head2,MergeTail总是指向min(head1.key,head2.key),同时MergeHead与head1,head2是同步前移的,直至head1或者head2有一个为NULL,则MergeTail直接指向另一个链表的head即可。具体可以看代码体会。
#include<iostream> using namespace std; struct node { int key; node* next; node(int k):key(k),next(NULL){} }; struct List { node* head; List():head(NULL){} }; node* ListInsert(node* head,node* z) { if(!head) { head=z; return head; } else { head->next=ListInsert(head->next,z); return head; } } node* MergeList(node* head1,node* head2) { if(!head1) return head2; if(!head2) return head1; node* MergeHead=NULL; node* MergeTail=NULL; //head1 && head2都存在 if(head1->key < head2->key ) { MergeHead = MergeTail = head1; head1=head1->next; } else { MergeHead = MergeTail = head2; head2=head2->next; } while(head1 && head2) { if(head1->key < head2->key) { MergeTail->next=head1; MergeTail=head1; head1=head1->next; } else { MergeTail->next=head2; MergeTail=head2; head2=head2->next; } } if(head1) MergeTail->next=head1; else MergeTail->next=head2; return MergeHead; } node* recurMergeList(node* head1,node* head2) { if(!head1)//如果没有head1 return head2; else if(!head2)//如果没有head2 return head1; node* MergeHead=NULL; if(head1->key < head2->key) { MergeHead=head1; MergeHead->next=recurMergeList(head1->next,head2); } else { MergeHead=head2; MergeHead->next=recurMergeList(head2->next,head1); } return MergeHead; } void main() { int A[]={1,3,4,5,9,11,16}; int B[]={2,6,12,13,15,17,19}; int len=sizeof(A)/sizeof(A[0]); List* LA=new List; for(int i=0;i<len;++i) { LA->head=ListInsert(LA->head,new node(A[i])); } List* LB=new List; for(int i=0;i<len;++i) { LB->head=ListInsert(LB->head,new node(B[i])); } node* p=LA->head; while(p) { cout<<p->key<<' '; p=p->next; } cout<<endl; p=LB->head; while(p) { cout<<p->key<<' '; p=p->next; } cout<<endl; //p=recurMergeList(LA->head,LB->head); p=MergeList(LA->head,LB->head); while(p) { cout<<p->key<<' '; p=p->next; } }
从一个未排序的链表中移除重复的项,增加一个条件:如果不允许使用临时的缓存,你如何解决这个问题?
思路:
1.如果可以使用额外空间,那就空间换时间,新建一个额外的数组保存之前出现过的项,下一次再出现同样的项,就删除一个.这种办法需要O(n)的空间和O(n)的时间.
2.如果不可以使用额外的空间,那可以考虑用蛮力法,用两个指针,每次扫描到一个结点,就用另一个指针扫描它后面的结点,删除重复项,时间效率是O(n^2).
3.如果可以先将链表排好序,那么删除重复结点的时间复杂度可以降到O(n),加上排序的复杂度,是O(nlgn+n).
以下是三种思路的实现:
思路1:
void RomoveRepeatNode1(node *head) { if(head==NULL) return; node *p=head, *q=head->next; int hash[10000]; memset(hash,0,sizeof(hash)); hash[head->key] = true; while(q) { if(hash[q->key]) { p->next = q->next; delete q; q = p->next; } else { hash[q->key] = true; p = q , q = q->next; } } }
思路2:
void RomoveRepeatNode2(node *head) { if(head==NULL) return; node *p=head,*q=NULL,*prec=NULL; //prec指向q的前趋 while(p) { q=p->next,prec=p; while(q) { if( p->key == q->key) { prec->next=q->next; delete q; q=prec->next; } else { prec=q,q=q->next; } } p=p->next; } }
思路3:我们可以用改良版的链式快排对链表进行快速排序.然后,只需要比较相较相邻的结点value是否相同,则选择删除与否即可.
链式快排:(从线性快排稍做改进可以得到)
node* partition(node* head,node* tail) { if( head==NULL ) return NULL; int pivot=head->key; node* p=head->next,*q=head; while(p != tail) { if( p->key < pivot ) { q=q->next; swap(q->key,p->key); } p=p->next; } swap(head->key,q->key); return q; } void QuickSort(node* l,node* r) { if( l != r) { node* pivot=partition(l,r); QuickSort(l,pivot); QuickSort(pivot->next,r); } }
void RomoveRepeatNode3(node *head) { QuickSort(head,NULL); node* pDeleted=NULL; while(head && head->next) { if( head->key == head->next->key ) { pDeleted = head->next; head->next = pDeleted->next; delete pDeleted; } head=head->next; } }
相关文章推荐
- LeetCode第22题--合并两个排序好的链表
- 13、合并两个排序好的链表
- LeetCodet题解--21. Merge Two Sorted Lists(合并两个排序好的链表)
- 合并两个排序的链表
- 《剑指offer》合并两个排序的链表
- 合并两个排序链表
- 合并两个排序的链表
- 合并两个排序的链表
- 剑指offer-- 合并两个排序的链表
- 【剑指Offer面试题】 九度OJ1519:合并两个排序的链表
- 剑指offer 面试题17 合并两个排序的链表-Java实现
- 算法-合并两个排序的链表
- 【剑指Offer面试编程题】题目1519:合并两个排序的链表--九度OJ
- 剑指offer--合并两个排序的链表
- 剑指offer-chapter3-面试题17-合并两个排序的链表(java)
- 合并两个排序的链表
- LintCode 合并两个排序链表
- 合并两个排序的链表
- 合并两个排序的链表
- 有a,b两个已按学号升序排序的链表,每个链表中的结点包括学号、成绩。要求把两个链表合并,仍按学号升序排列。