2.链表
2.1.1 尾插算法
1.逆转链表的算法实现(同时考虑用递归方式实现逆序输出)
void reversel(LNode *L) //头插法
{
LNode *p = L->next,*q;
L->next = NULL;
while(p)
{
q = p->next;
p->next = L->next;
L->next = p;
p = q;
}
}
//顺序表的逆置实现
void reversel(Sqlist &L)
{
int i,j;
int temp;
for(i=0,j=L.length-1;i<j;i++,j--)
{
temp = L.data[i];
L.data[i] = L.data[j];
L.data[j] = temp;
}
}
2.将一个升序单链表和一个降序单链表合并为一个有序链表,要求算法时间复杂度为O(m+n)。
降序链表头插法逆序后,两个升序链表合并,时间复杂度为max(m,n),则总时间复杂度为O(m+n).
int merge(LNode *L1,LNode *L2,LNode *&C) //假设L1为升序 L2为逆序
{
reversel(L2); //L2变升序
LNode *p = L1->next;
LNode *q = L2->next;
LNode *s,*rear;
C=L1;rear = C;
L1->next = NULL;
free(L2);
while(p&&q)
{
if(p->data<=q->data)
{
s = p;p = p->next;
s->next = NULL;
rear->next = s;
rear = rear->next;
}
else
{
s = q;q = q->next;
s->next = NULL;
rear->next = s;
rear = rear->next;
}
}
if(p)
rear->next = p;
else if(q)
rear->next = q;
}
2.1.2 双指针算法
1.编写算法找到两个单链表的公共结点。
大小分别为m,n,(m>n),m-n=k,则L1从L1+k处开始走,L2从L2处开始走,两者相等则当前以及后面的结点为公共结点。
LNode *findCommonNode(LNode *L1,LNode *L2)
{
//统计L1 L2的长度
int n1=0,n2=0;
LNode *p1,*p2;
p1 = L1->next;
p2 = L2->next;
while(p1)
{
n1++;p1 = p1->next;
}
while(p2)
{
n2++;p2 = p2->next;
}
int k,i; //寻找起点
if(n1>n2)
{
k = n1-n2;
i=0
while(p1)
{
i++;
if(i<k+1) p1=p1->next;
else
break;
}
}
else
{
k = n2-n1;
i=0
while(p2)
{
i++;
if(i<k+1) p2=p2->next;
else
break;
}
}
while(p1&&p2)
{
if(p1->data!=p2->data)
{
p1 = p1->next;p2 = p2->next;
}
else
break;
}
return p1;
}
2.定位到链表中倒数k个的结点。
双指针。
int findElem(LNode *head,int k)
{
LNode *p1 = head->next;
LNode *p = head;
int i=1;
while(p1!=NULL)
{
p1 = p1->next;
i++;
if(i>k)
p = p->next;
}
if(p==head) return 0;
else
{
cout<<p->data<<endl;
return 1;
}
}
3.给定一个链表,
(1)判断链表中是否有环。
(2)若有环,求环的长度
(3)求开始入环的第一个节点
解:1.双指针,一个跑一步,一个跑两步,有环则会相遇,否则快的会跑到null,为尾节点。
int isCircle(LNode *L)
{
LNode *p1,*p2;
p1 = L->next;
p2 = L->next->next;
while(p2!=NULL)
{
if(p1->data!=p2->data)
{
p1 = p1->next;
p2 = p2->next->next;
}
else
break;
}
if(p2!=NULL)
return 1;
else
return 0;
}
(2).快指针为s,慢指针为q,则当两者相遇时,快指针停在当前位置,慢指针继续走,相遇时走过的步数为环的长度。
int circleLength(LNode *L)
{
LNode *s,*q;
s = L->next;
q = L->next->next;
while(q!=NULL)
{
if(s->data != q->data)
{
s = s->next;
q = q->next->next;
}
else
break;
}
if(q==NULL)
return 0;
int len=0;
s = s->next;
while(s->data!=q->data)
{
len++;
s = s->next;
}
return len;
}
(3).接1题,两者相遇时,慢指针从当前位置每次一步走,快指针从链表头位置每次一步的走,两者再次相遇的位置节点为入环的第一个节点。
4.请判断一个链表是否为回文链表。
解:思路1:使用栈
思路2:双指针,找到中间节点,将中间节点后面的节点反转,从变形链表的两头向中间靠近,依次对比节点,两指针相遇则成立。
int dc(LinkedList h,int n)
//h是带头结点的n个元素单链表,链表中结点的数据域是字符。本算法判断链表是否是中心对称。
{
char s[];int i=1;//i记结点个数, s字符栈
p=h->next;//p是链表的工作指针,指向待处理的当前元素。
for(i=1;i<=n/2;i++)//链表前一半元素进栈。
{s[i]=p->data;p=p->next;}
i--; //恢复最后的i值
if(n%2==1)
p=p->next;若n是奇数,后移过中心结点。
while(p!=null && s[i]==p->data)
{i--;p=p->next;} //测试是否中心对称。
if(p==null)return(1);//链表中心对称
else return(0); ∥链表不中心对称
}
2.2.2 集合算法
1.求线性表A和B的交集 A B 都为非递减
void intersection(LNode *A,LNode *B,LNode *&L) //求AB的交集
{
LNode *pa,*pb,*pl;
pa = A->next;
pb = B->next;
L = A;
L->next = NULL;
free(B); //释放B头节点空间
pl = L;
while(pa&&pb)
{
if(pa->data<pb->data)
pa = pa->next;
else if(pb->data<pa->data)
pb = pb->next;
else
{
pl->next = pa;
pl = pl->next;
pa = pa->next;
pb = pb->next;
}
}
pl->next = NULL;
}
2.求线性表 A B的并集
void union(LNode *A,LNode *B,LNode *&L) //A B 都是非递减
{
LNode *pa,*pb,*pl;
pa = A->next;
pb = B->next;
L = A;
L->next = NULL;
free(B); //释放B头节点空间
pl = L;
while(pa&&pb)
{
if(pa->data<pb->data)
{
pl->next = pa;
pl = pl->next;
pa = pa->next;
}
else if(pb->data<pa->data)
{
pl->next = pb;
pl = pl->next;
pb = pb->next;
}
else
{
pl->next = pa;
pl = pl->next;
pa = pa->next;
pb = pb->next;
}
}
if(pa)
pl->next = pa;
else if(pb)
pl->next = pb;
else
pl->next = NULL;
}
3.求 A — B (差集)
void differSet(LNode *A,LNode *B,LNode *&L)
{
LNode *pa,*pb,*p,*pl;
pa = A->next;
pb = B->next;
pl = L = A;
L->next = NULL;
free(B);
while(pa&&pb)
{
if(pa->data<pb->data)
{
p = pa;
pa = pa->next;
p->next = pl->next;
pl->next = p;
pl = pl->next;
}
else if(pa->data==pb->data)
{
pa = pa->next;
pb = pb->next;
}
else
pb = pb->next;
}
while(pa)
{
p = pa;
pa = pa->next;
p->next = pl->next;
pl->next = p;
}
pl->next = NULL;
}
4.删除链表中重复的元素
//当链表是有序排列时
void deleteSame(LNode *L)
{
LNode *p,*t;
p = L->next;
while(p->next!=NULL)
{
if(p->data==p->next->data)
{
t = p->next;
p->next = p->next->next;
free(t);
}
p = p->next;
}
}
//当链表是无序排列时
void deleteSame(LNode *L)
{
LNode *p,*pre,*pt,*t;
p = L->next;
while(p)
{
pre= L;
pt = L->next;
while(pt)
{
if(pt!=p &&pt->data == p->data)
{
t = pt;
pre->next = pt->next;
pt = pt->next;
free(t);
}
else
{
pre = pt;
pt = pt->next;
}
}
p = p->next;
}
}
2.2.3 链表排序
1.将一个元素插入有序链表中。
void insertElem(LNode *L,ElemType x)
{
LNode *p,*pre;
pre = L;p = L-next;
while(p)
{
if(p->data<x)
{
pre = p;
p = p-next;
}
break;
}
LNode pt = (LNode *)malloc(sizeof(LNode));
pt->data = x;
pt->next = pre->next;
pre->next = pt;
}
2.将链表插入排序
LinkedList LinkListSort(LinkedList list)
//list是不带头结点的线性链表,链表结点构造为data和link两个域,data是数据域,link是指针域。本算法将该链表按结点数据域的值的大小,从小到大重新链接。
{
p=list->link; //p是工作指针,指向待排序的当前元素。
list->link=null; //假定第一个元素有序,即链表中现只有一个结点。
while(p!=null)
{
r=p->link; //r是p的后继。
q=list;
if(q->data>p->data)//处理待排序结点p比第一个元素结点小的情况。
{
p->link=list;
list=p;//链表指针指向最小元素。
}
else//查找元素值最小的结点。
{
while(q->link!=null&&q->link->data<p->data)
q=q->link;
p->link=q->link;//将当前排序结点链入有序链表中。
q->link=p;
}
p=r;//p指向下个待排序结点。
}
}
3.对链表进行选择排序
//实现对链表head进行选择排序的算法
typedef struct node {char data; struct node *link; }node;
node *select(node *head)
{node *p,*q,*r,*s;
p=(node *)malloc(sizeof(node));
p->link=head; head=p;
while(p->link!=null)
{q=p->link; r=p;
while (q->link!=NULL)
{ if (q->link->data<r->link->data) r=q;
q=q->link;
}
if (r!=p) {s=r->link; r->link=s->link; s->link= (p->link); (p->link=s);}
(p=p->link) ;
}
p=head; head=head->link; free(p); return(head);
}
4.设有一头指针为L的带有表头结点的非循环双向链表,其每个结点中除有pred(前驱指针),data(数据)和next(后继指针)域外,还有一个访问频度域freq。在链表被起用前,其值均初始化为零。每当在链表中进行一次Locate(L,x)运算时,令元素值为x的结点中freq域的值增1,并使此链表中结点保持按访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点的最后,以便使频繁访问的结点总是靠近表头。
DLinkList locate(DLinkList L,ElemType x)
//L是带头结点的按访问频度递减的双向链表,本算法先查找数据x,查找成功时结点的访问频度域增1,最后将该结点按频度递减插入链表中适当位置。
{
DLinkList p=L->next,q; //p为L表的工作指针,q为p的前驱,用于查找插入位置。
while (p && p->data !=x) p=p->next; //查找值为x的结点。
if (!p)
{printf(“不存在值为x的结点\n”); exit(0);}
else {
p->freq++; //令元素值为x的结点的freq域加1 。
p->next->pred=p->pred; //将p结点从链表上摘下。
p->pred->next=p->next;
q=p->pred; //以下查找p结点的插入位置
while (q !=L && q->freq<p->freq)
q=q->pred;
p->next=q->next;
q->next->pred=p; //将p结点插入
p->pred=q;
q->next=p;
}
return(p); //返回值为x的结点的指针
}
2.2.4 链表的分拆
1.将一个链表分解为一个奇数链表和一个偶数链表。
void DisCreat3(LinkedList A)
//A是带头结点的单链表,本算法将其分解成两个带头结点的单链表,A表中含原表中序号为奇数的结点,B表中含原表中序号为偶数的结点。链表中结点的相对顺序同原链表。
{
i=0;//i记链表中结点的序号。
//B=(LinkedList)malloc(sizeof(LNode);//创建B表表头。
B->next=null; //B表的初始化。
LinkedList ra,rb;//ra和rb将分别指向将创建的A表和B表的尾结点。
ra=A;rb=B;
p=A->next; //p为链表工作指针,指向待分解的结点。
A->next=null; //置空新的A表
while(p!=null)
{
r=p->next; //暂存p的后继。
i++;
if(i%2==0) //处理原序号为偶数的链表结点。
{
p->next=rb->next;//在B表尾插入新结点;
rb->next=p;
rb=p;//rb指向新的尾结点;
}else//处理原序号为奇数的结点。
{
p->next=ra->next;
ra->next=p;
ra=p;
}
p=r; //将p恢复为指向新的待处理结点。
}
}
2.编写函数将一整数序列中所有负数移到所有正数之前
int Rearrange(SeqList a; int n)
//a是具有n个元素的线性表,以顺序存储结构存储,线性表的元素是整数。本算法重排线性表a,使所有值为负数的元素移到所有值为正数的数的前面。
{
i=0; j=n-1; //i,j为工作指针(下标),初始指向线性表a的第1个和第n个元素。
t=a[0]; //暂存枢轴元素。
while(i<j)
{
while(i<j && a[j]>=0)
j--; //若当前元素为大于等于零,则指针前移。
if(i<j)
{
a[i]=a[j];
i++;
} //将负数前移。
while(i<j &&a[i]<0)
i++; //当前元素为负数时指针后移。
if(i<j)
a[j--]=a[i]; //正数后移。
}
a[i]=t; //将原第一元素放到最终位置。
}
3.给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
链表长度为N,将第N/2个结点作为根节点,并以该节点划分链表为左右两个链表,将左边链表的中间节点作为左节点,右链表的中间节点作为右节点,递归执行。
BTNode* tranTobtree(LNode *L,int n)
{
if(n/2)
return NULL;
else
{
int mid,ln,rn;
if(n%2==0) //偶数
mid = n/2;
else //奇数
mid = n/2+1;
ln = mid - 1;
rn = n - mid;
BTNode *b;
b = (BTNode *)malloc(sizeof(BTNode));
//查找当前的根节点
int i; i=0;
LNode *p;p=L->next;
while(p)
{
i++;
if(i==mid)
break;
p=p->next;
}
b->data = p->data;
b->lchild = tranTobtree(L,ln);
b->rchild = tranTobtree(p->next,rn);
return b;
}
}
2.2.5 链表计数
1.设键盘输入n个英语单词,输入格式为n, w1, w2,…,wn,其中n表示随后输入英语单词个数,试编一程序,建立一个单向链表,实现:
(1)如果单词重复出现,则只在链表上保留一个。
(2)除满足(1)的要求外。链表结点还应有一个计数域,记录该单词重复出现的次数,然后输出出现次数最多的前k(k<=n)个单词。
typedef struct node
{
int freg;//频度域,记单词出现的次数。
char word[maxsize];//maxsize是单词中可能含有的最多字母个数。
struct node *next;
}node, *LinkedList;
(1)
LinkedList creat()
//建立有n(n>0)个单词的单向链表,若单词重复出现,则只在链表中保留一个。
{
LinkedList la;
la=(LinkedList)malloc(sizeof(node));//申请头结点。
la->next=null; //链表初始化。
for(i=1;i<=n;i++) //建立n个结点的链表
{
scanf(“%s”,a); //a是与链表中结点数据域同等长度的字符数组。
p=la->next;pre=p; //p是工作指针,pre是前驱指针。
while(p!=null)
if(strcmp(p->data,a)==0)
{p->freg++;break;} //单词重复出现,频度增1。
else {pre=p;p=p->next;} //指针后移。
if(p==null) //该单词没出现过,应插入。
{
p=(LinkedList)malloc(sizeof(node));
strcopy(p->data,a);p->freg=1;p->next=null;pre->next=p;
} //将新结点插入到链表最后。
}
return(la);
}
(2)
void CreatOut()
//建立有n个单词的单向链表,重复单词只在链表中保留一个,最后输出频度最高的k个单词。
{
LinkedList la;
la=(LinkedList)malloc(sizeof(node));//申请头结点。
la->next=null; //链表初始化。
for(i=1;i<=n;i++) //建立n个结点的链表
{
scanf(“%s”,a); //a是与链表中结点数据域同等长度的字符数组。
p=la->next;pre=p; //p是工作指针,pre是前驱指针。
while(p!=null)
if(strcmp(p->data,a)==0)
{ p->freg++; //单词重复出现,频度增1。
pre->next=p->next; //先将p结点从链表上摘下,再按频度域值插入到合适位置
pre=la; q=la->next;
while(q->freg>p->freg)
(pre=q; q=q->next; )
pre->next=p; p->next=q; //将p结点插入到合适位置
}
else {pre=p;p=p->next;} //指针后移。
if(p==null) //该单词没出现过,应插入到链表最后。
{
p=(LinkedList)malloc(sizeof(node));
strcopy(p->data,a);p->freg=1;p->next=null;pre->next=p;
}//if 新结点插入。
}//结束for循环建表。
int k,i=0;
scanf(“输入要输出单词的个数%d”,&k);
p=la->next;
while (p && i<k) ∥输出频度最高的k个单词
{
printf(“第%3d个单词%s出现%3d次\n”,++i,p->data,p->freg);
p=p->next;
}
if (!p)
printf(“给出的%d值太大\n”,k);
}
2.删除链表中绝对值相等的点,|data|<n
类似哈希表的用法,以空间换时间。
void deleteSame(LNode *L)
{
int i,S[n];
for(i=0;i<=n;i++) S[i] = 0;
SNode *p,*pre,*t;
p = L->next;pre = L;
while(p)
{
if(S[p->data]==0)
{
S[p->data] = 1;
pre = p;
p = p->next;
}
else
{
t=p;p=p->next;
pre->next = p->next;
free(t);
}
}
}
3.已知长度为n的线性表A采用顺序存储结构,请写一时间复杂度为0(n)、空间复杂度为0(1)的算法,该算法删除线性表中所有值为item的数据元素
void Delete(ElemType A[ ],int n)
//A是有n个元素的一维数组,本算法删除A中所有值为item的元素。
{
i=1;j=n;设置数组低、高端指针 下标
while(i<j)
{
while(i<j && A[i]!=item)i++; //若值不为item,左移指针。
if(i<j)
while(i<j && A[j]==item)j--;//若右端元素值为item,指针左移
if(i<j)A[i++]=A[j--];
}
4.建立双向链表实现二进制数的加减,注意溢出和进位。
双向链表,进位则增加前面的节点