链表练习
2010-12-22 22:32 Clingingboy 阅读(888) 评论(0) 编辑 收藏 举报一.单链表
基本数据结构预留
#include "stdafx.h" #include <iostream> using namespace std; #include <string> #include <queue> class Link { public: Link(int iata) { iData=iata; } int iData; Link* Next; void DisplayLink() { cout << iData << endl; } }; class LinkList { private: Link* first; public: void InsertFirst(int iData) { Link* newLink=new Link(iData); if(IsEmpty()) { first=newLink; first->Next=NULL; } else { newLink->Next=first; first=newLink; } } bool IsEmpty() { return first==NULL; } Link* GetFirstNode() { return first; } Link* GetLastNode() { Link* currentLink=first; while(currentLink->Next&& !IsEmpty()) currentLink=currentLink->Next; return currentLink; } LinkList(){first=NULL;} }
1.删除值为n的节点的直接前驱节点
p=>r=>n变为p=>r
思路:由于是单链表没有前驱,所以要提前记录,并且在寻找后继的过程也要做记录,若找到则将更改节点的前驱的前驱的后继(即节点前驱)
void DeleteBefore(int iData) { //default val Link* p=first; Link* q=first->Next; Link* r; if(q!=NULL) r=q->Next; //find while(r!=NULL && r->iData!=iData) { p=q; q=r; r=r->Next; } //delete if(r!=NULL) { p->Next=q->Next; //p->Next=r; delete q; } }
Test
LinkList list; list.InsertFirst(4); list.InsertFirst(1); list.InsertFirst(2); list.InsertFirst(3); list.DeleteBefore(1);
2.设置C={a1,b1,a2,b2…,an,bn}以hc单链表存放,将其拆分为2个链接({a1,a2…,an}{b1,b2…,bn}),可以理解为奇偶拆分
static void Split(LinkList* ha,LinkList* hb,LinkList* hc) { //3,2,1,4=>3,2 1,4 Link* p=hc->GetFirstNode(); Link* ra; Link* rb; while(p!=NULL) { //add first link if(ha->IsEmpty()) { ha->InsertFirst(p->iData); ra=ha->GetFirstNode(); } else { ra->Next=p; ra=p; } //add second link p=p->Next; if(p!=NULL) { if(hb->IsEmpty()) { hb->InsertFirst(p->iData); rb=hb->GetFirstNode(); } else { rb->Next=p; rb=p; } p=p->Next; } } //remove end link's next ra->Next=rb->Next=NULL; }
3.将两个有序单链表归并为一个有序单链表
思路:与顺序表其实是一样的,前提还是有序不重复,最后的时候记得有没有合并的加一个链接,这里的效率好一些.
static void Merge(LinkList* ha,LinkList* hb,LinkList* hc) { Link* p=ha->GetFirstNode(); Link* q=hb->GetFirstNode(); Link* r; while(p!=NULL && q!=NULL) { if(p->iData<q->iData) { if(hc->IsEmpty()) { hc->InsertFirst(p->iData); r=hc->GetFirstNode(); } else { r->Next=p; r=p; } p=p->Next; } else if(p->iData>q->iData) { if(hc->IsEmpty()) { hc->InsertFirst(q->iData); r=hc->GetFirstNode(); } else { r->Next=q; r=q; } q=q->Next; } } if(q!=NULL) { p=q; } if(p!=NULL) { hc->GetLastNode()->Next=p; } }
4.设置单链表L为递增有序,设计一个算法删除表中值大于min或者小于max的节点
由于是有序的,只要找出第一个大于min的节点和小于max的节点,然后连接起来就可
因为没有前驱,还是需要一个暂存变量
void SortOutRange(int min,int max) { //1,2,3,4,5,6=>1,2,6 Link* start=this->GetFirstNode(); Link* temp=start; while(temp!=NULL && temp->iData<min) { start=temp; temp=temp->Next; } Link* end=temp; while(end!=NULL && end->iData<=max) { end=end->Next; } start->Next=end; //free }
5.同上,但链表改为非有序
由于不是有序,那么就导致若每次遇到范围内则删除之,即有删除就要移位.复杂度为O(n)
这里本应该记录被删除节点的前驱的,偷懒省去了
void OutRange(int min,int max) { //1,3,2,5,8,6=>1,2,8,6 Link* p; Link* temp=this->GetFirstNode(); while(temp!=NULL) { p=temp; temp=temp->Next; if(p->iData>=min && p->iData<=max) { this->DeleteLink(p); } } //free }
6.有序递增,删除值域重复的结点
由于是有序,那么值域重复肯定是相邻的
void SortDistinct() { //1,2,2,4,5,5,6,6=>1,2,4,5,6 Link* p=this->GetFirstNode(); if(p==NULL) return; while(p->Next!=NULL) { if(p->iData==p->Next->iData) { p->Next=p->Next->Next; } else { p=p->Next; } } }
7.同上,非有序删除值域重复的结点(复杂度为O(n的平方)
无序的话,就得从第1个节点与其他节点都比较一遍,每次循环则减少一次比较的次数
比如1,2,3=>1,2 1,3=>2,3(因为1,2比较过了,即取无重复的2个数字组合)
void Distinct() { //1,2,2,4,5,5,6,6=>1,2,4,5,6 Link* p=this->GetFirstNode(); Link* r; Link* temp; while(p!=NULL) { r=p->Next; while(r!=NULL) { temp=r; r=r->Next; if(p->iData==temp->iData) { this->DeleteLink(temp); } } p=p->Next; } }
都是删除的方法,思路还是很相似的
8.判定一个单链表是否单调递增
很简单只要一个节点的后驱比其自身小的话就算不是递增
bool IsIncrease() { Link* p=this->GetFirstNode(); while(p->Next!=NULL) { if(p->iData>p->Next->iData) return false; else p=p->Next; } return true; }
9.为单链表排序单调递增
即冒泡排序
10.删除单链表最小值
即找出最小值,一遍比较一遍存,然后删除即可,关键同时还要记录其前驱
记录一组寻常的节点和其前驱和最小值的和其前驱
void Deletemin() { Link* pre=this->GetFirstNode(); Link* p=pre->Next; Link* min=p; Link* minpre=pre; while(p!=NULL) { //min link and its pre link if(p->iData<min->iData) { min=p; minpre=pre; } //pre and current link pre=p; p=p->Next; } minpre->Next=min->Next; delete min; }
11.倒置(reverse)
解法1:用stack,练习用的
void Reverse() { InsertFirst(1); InsertFirst(2); InsertFirst(3); //放入queue,先进后出 queue<int> stack; Link* currentLink=this->GetFirstNode(); Link* lastLink=this->GetLastNode(); while(currentLink!=NULL) { stack.push(currentLink->iData); currentLink=currentLink->Next; } //3,2,1 this->ClearList(first); first=NULL; while(!stack.empty()) { int a=stack.front(); this->InsertFirst(a); stack.pop(); } currentLink=this->GetFirstNode(); }
解法2 就地倒置
思路:
1.因为是就地倒置,所以每个向前的元素每次都成为第一个元素
2.然后将该元素的前驱指向原先的第一个元素
void Reverse1() { //4,3,2,1=>1,2,3,4 //3,4,2,1 //2,3,4,1 //1,2,3,4 Link* prev=first; Link* r; first=NULL; while(prev!=NULL) { //r is temp store=>prev next r=prev->Next; //exchange preve and next prev->Next=first; //change firt link first=prev; //change prev link prev=r; } }
方案2:
基于上面的基础,把原链表的尾结点暂存起来,而不是每次更改首结点,等循环结束以后给首结点赋值,其他保持不变
void Reverse2() { //4,3,2,1=>1,2,3,4 //3,4,2,1 //2,3,4,1 //1,2,3,4 Link* prev=first; Link* current=prev->Next; prev->Next=NULL; Link* r; while(current!=NULL) { //r is temp store=>prev next r=current->Next; //exchange preve and next current->Next=prev; //change firt link prev=current; //change prev link current=r; } first=prev; }
12.两个集合的交、差、并集
1