2.3
pre
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}LNode,*LinkList;
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode, *DLinklist;
2.3.02
在带头结点的单链表L中,删除所有值为x的结点,并释放其空间,假设值为x的结点不唯一,试编写算法以实现上述操作。
void del_x(LinkList &L, ElemType x)
{
LNode *p = L->next, *pre = L, *q;//q是临时节点,好像就要用指针
while(p!=NULL){
if(p->data == x){
q = p;
p = p->next;
pre->next = p;
free(q);
}
else{
pre = p;
p = p->next;
}//else
}//while
}
2.3.03
设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值
void print_r(LinkList &L)
{
if(L->next!=NULL) print_r(L->next);
if(L!=NULL) printf("%d",L->data);
}
void reverse(LinkList &L)
{
if(L!=NULL) print_r(L);
return;
}
2.3.05
试编写算法将带头结点的单链表就地逆置,也就是空间复杂度为O(1)
//解法一,拆下来以后用头插法反插
LinkList reverse(LinkList &L)
{
LNode *p,*r,*pre;
p = L->next;L->next=NULL;
while(p->next!=NULL){
q = p->next;
p->next = L->next;
L->next = p;
p = q;
}
return L;
}
//解法二,反向插,最后将位置指向L,两个时间复杂度都是O(n),空间复杂度为O(1)
LinkList reverse(LinkList &L)
{
LNode *p,*r,*pre;
p = L->next; r = p->next; p->next = NULL;
while(r!=NULL){
pre = r->next;
r->next = p;
p = r;
r = pre;
}
L->next = p;
return L;
}
2.3.08
给定两个单链表,编写算法找出两个链表的公共结点
分析:如果存在公共结点,那两个链表的结尾都是相同的,所以可以先计算两个链表的长度,再减去插值寻找公共结点,时间复杂度为O(length1+length2)
LinkList find_1st(LinkList &L1, LinkList &L2)
{
int len1 = Length(L1), len2 = Length(L2);
int dist;
LinkList p,q;
if(len1>len2){
p = L1->next; q = L2->next;
dist = len1-len2;
}
else{
p = L2->next; q = L1->next;
dist = len2-len1;
}
while(dist--){
p = p->next;
}
while(p!=NULL){
if(p==q)//真的可以这么写吗?
return p;
else{
p = p->next;
q = q->next;
}
}
return NULL;
}
2.3.11
将一个带头结点的单链表A分解为两个带头结点的单链表B和C,使得B表中含有原表中序号为奇数的元素,而C表中含有原表中序号为偶数的元素,且C表中顺序反过来,并保持相对顺序
思路:没啥区别,练练链表的手。有个很有意思的地方,就是这个怎么返回两个链表的同时读入一个链表,答案的做法是保留原来输入的链表作为其中一个结果进行输出,另一个用函数返回值进行返回。
LinkList divide(LinkList &A )
{
LinkList B = (LinkList)malloc(sizeof(LNode));
B->next = NULL;
LNode *p=A->next, *q;
LNode *tmp = A;//指向A的尾结点
while(p!=NULL){
tmp->next = p; tmp = p;
p = p->next;
if(p!=NULL){
q = p->next;
p->next = B->next;
B->next = p;
p = q;
}
}
tmp->next = NULL;
return B;
}
LinkList divide_myself(LinkList &A)
{
LinkList B = (LinkList)malloc(sizeof(LNode));
B->next = NULL;
LNode *pr = A;
LNode *cr = A->next;
while(cr!=NULL){
cr = cr->next;pr = pr->next;
if(cr!=NULL){
pre->next = cr->next;
cr->next = B->next;
B->next = cr->next;
cr = pre->next;
}
}
cr->next =NULL;//如果是奇数的情况下进行的定义
return B;
}
2.3.16
两个整数序列A=a1,a2,…,am和B=b1,b2,…,bn已经存入两个单链表中,设计一个算法判断B是否是A的连续子序列
int child_series(LinkList &A, LinkList &B)
{
LNode *a = A, *b = B;
LNode *p = a;//用于存档
while(a&&b){
if(a->data == b->data){
a = a->next;
b = b->next;
}
else{
b = B;//回到表头
p = p->next;
a = p;
}
}//while
if(b == NULL) return 1;
else return 0;
}
2.3.20
设头指针为L的带有表头结点的非循环双向链表,其每个结点中除有prior(前驱指针),data(数据)和next(后继指针)域外,还有一个访问频度域freq.在链表被启用前,其值均初始化为零,每当在链表中进行一次Locate(L,x)运算时,令元素值为x的结点中freq域的值增1,并使此链表中结点保持按访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点前面,以便使频繁访问的结点总是靠近表头.试编写符合上述要求的Locate(L,x)运算的算法,该运算为函数过程,返回找到结点的地址,类型为指针型.
void Locate(LinkList &L, ElemType x)
{
LNode *s = L;
LNode *sr = L, tmp;
while(s!=NULL){
if(s->data != x) s = s->next;
else s->freq++;
}
if(s==NULL)//not found
return -1;
else//change the place
{
while(sr->freq>s->freq&&sr!=NULL) sr = sr->next;
if(sr!=NULL){//exist prior place
s->pred->next = s->next;
s->next->pred = s->pred;
sr->pred->next = s;
s->pred = sr->pred;
s->next = sr;
sr->pred = s;
}// end if
}//end else
}
//也可以考虑从s开始往前遍历,这样的效率更高但是我不想再写一版了。
2.3.22
已知一个带有表头结点的单链表,结点结构为 data link 假设该链表只给出了头指针list。在不改变链表的前提下,请设计一个尽可能高效的 算法,查找链表中倒数第k个位置,如果成功则输出该点的data域的值,并返回1,否则返回0
-
基本思路
直接取两个节点,一个在后面k个位置,后面那个到NULL了前面那个刚好再倒数第k个位置。
-
代码
int find_reverse_k(int k, LinkList &L)
{
LNode *pre = L, *lat = L;
for(int i = 0; i<k; i++){
lat = lat->next;
if(lat == NULL) return 0;
}
while(lat!=NULL){
lat = lat->next;
pre = pre->next;
}
printf("%d",pre->data);
return 1;
}
2.3.25
设线性表L=(a1,a2,a3,…,an-2,an-1,an),采用带头节点的单链表保存。设计算法,重新排列L中的节点,得到线性表L’=(a1,an,a2,an-1,a3,an-2,…)。
-
思路
我想的是先建立头结点,然后先用尾插法把后半部分给插上,再正向进行一个隔一个的差。这样的时间复杂度是O(n),空间复杂度是O(1)
-
(typora的换行真的是一坨沈阳美食家)代码
LinkList reshape(LinkList &L)
{
LinkList B = (LinkList)malloc(sizeof(LNode));
LNode *tmp;
B->next = NULL;
LNode *mid=L->next, *fin=L->next;
int length=0;//the length of line
//first find the mid place
while(fin!=NULL){
mid = mid->next;
fin = fin->next;
if(fin!=NULL){
fin = fin->next;
length+=2;
}
else length+=1;
}
// mid is deemed to be the later part and without the mid point
while(mid!=NULL)
{
tmp = mid;
mid->next = B->next;
B->next = mid;
mid = tmp->next;
}
// after this, it is (an,an-1,...a(n/2))
tmp = L->next; fin = tmp->next; mid = B;
for(i = 0; i<(length+1)/2; i++){
tmp->next = mid->next;
mid->next = tmp;
mid = mid->next->next;
tmp = fin;
fin = fin->next;
}
return B;
}
//当然,也可以像答案那样直接把后半部分进行逆置,然后前后加,也可以得到一样的结果
void change_list(Node *h)
{
Node *p,*q,*r,*s;
p = q = h;
while(q->next != NULL){
p = p->next;
q = q->next;
if(q->next != NULL) q = q->next;
}
q = p->next;//p所指节点为中间节点,q为后半段链表的首节点,我的做法提前next了,所以不一样
p->next = NULL;
while(q!=NULL){//将链表后半段逆置
r = q->next;
q->next = p->next;
p->next = q;
q = r;
}
s = h->next;
q = p->next;
p->next = NULL;
while(q!=NULL)
{
r = q->next;
q->next = s->next;
s->next =q;
s = q->next;
q = r;
}
}
后记
链表有点难,但是很suang口,写到后面还是有点high的