zoukankan      html  css  js  c++  java
  • 王道课后题之链表

    1.递归删除所有值为x的节点

    void Del_X(LinkList &L,int x){
        if(L == null) return;      //递归出口
        if(L->next != x){
            Del_X(L->next,x);   //若L指的节点不为x,那么就继续向后走
            return;
        }
    
        LNode *p;       //创建指向要删除的节点
        p = L;          //p指向本层节点
        L = L->next;
        delete(p);
        Del_X(L->next,x);   //递归调用
    }
    

    2.删除所有值为x的节点(没要求递归)

    思路:创建一个p和pre指针,一个指向当前节点,一个指向前一个节点

    如果遇到相同,那么就"跨越"这个节点

    void Del_X(LinkList &L,int x){
        LNode *p=L->next,*pre=L,*q;     //初始p和pre
    
        while(p != NULL){
            if(p->data == x){
                q = p;              //q指向p
                p = p->next;                //p继续向前走
                pre->next = p;       
                /*
                pre指向p 从而跨越了中间的节点,实现删除的效果
                */
                delete q;
            }else{
                pre = p;     // 正常往前走
                p = p->next;
            }
        }     
    }
    

    3.反向输出链表

    思路:一直递归到最后,从而输出从里层往外输出

    void Rever(LinkList &L){
        
        if(L->next != NULL){
            Rever(L->next);     //一直递归到最后
        }     
        cout << L->data;
    }
    

    4.删除链表中最小元素

    思路:维护最小值指针和他的前驱指针,遍历记录最小值,最后删除

    void DeleMin(LinkList &L){
        
        LNode *pre=L,*p=pre->next;
        LNode *minpre=pre,*minp=p;      //记录最小值得节点以及他的前驱
    
        while(p != NULL){       
            if(p->data < minpre->data){     //后一个比前一个小
                min = p;            //维护min处于最小节点位置
                minpre = pre;       //维护minpre处于最小节点的前驱
            }
            p = p->next;        //大步往前走
            pre = pre->next;    //大步往前走
        }
        minpre->next = min->next;    //删除最小值
        free(minp);
        return L;
    }
    

    5.链表就地反转

    思路:维护两个指针,cur和pre,让pre永远在cur的前方

    ListNode reverseList(ListNode head) {
            ListNode *cur = NULL, *pre = head;
            while (pre != NULL) {
                ListNode *t = pre->next;         //再存pre的下一个指针          
                pre->next = cur;                  //pre指向cur
                cur = pre;                        //cur往前走一步
                pre = t;                        //pre往前走一步
            }
            return cur;
        }
        
    

    6.重排链表,使其变成递增序列

    思路:使用插入排序每次遍历到的节点,插入到前面的有序表中

    void Sort(LinkList &L){
            LNode *p = L->next;
            LNode *r = p->next;     //保存p的后继,防止断链
            LNode *pre;
            p->next = NULL;     //构造第一个有序表
            p = r;
     
            while(p != NULL){
                r = p->next;
                pre = L;
                while(pre->next != NULL && pre->next->data < p->data){
                    pre = pre->next;        //pre往前走,寻找插入p的前驱结点
                }
                p->next = pre->next;    //将*p插入到*pre之后
                pre->next = p;
                p = r;
            }
     
        }
    

    7.删除介于两个值之间的所有节点(类似上一篇的线性表有道题)

    思路:维护一个节点的前驱指针,要删除的时候使用前驱指针

    void RangeDel(LinkList &L,int min,int max){
            LNode *pr = L;
            LNode *p = L->next;
     
            while(p != NULL){
                if(p->data > min && p->data < max){
                    pr->next = p->next;     //跨越这个节点,准备卸磨杀驴
                    free(p);
                    p = pr->next;       //p继续向前走
                }else{
                    pr = p;
                    p = p->next;
                }
            }
     
        }
    

    8.寻找两个链表的公共结点

    思路:先让腿长的人先跑前面的差程,然后和腿短的人一起跑,一起寻找交叉点

    LinkList SearchList(LinkList L1,LinkList L2){
    	int len1 = Length(L1);
    	int len2 = Length(L2);
    	int dist;
    	LinkList longLis,shortList;		//指向较长和较短的链表
    	if(len1 > len2){	//L1长
    		longLis = L1->next;
    		shortList = L2->next;
    		dist = len1 - len2;
    	}else{
    		longLis = L2->next;
    		shortList = L1->next;
    		dist = len2 - len1;
    	}
    	
    	while(dist--){
    		longLis = longLis->next;	//先让腿长的先跑
    	}
    	
    	while(longLis != NULL){
    		if(longLis == shortList)	return longLis;
    		else{			//寻找那个同时跑的 起跑线
    			longLis = longLis->next;
    			shortList = shortList->next;
    		}
    	}
    	return NULL;
    	
    }
    

    9.升序输出链表

    思路:每次找m最小值,从而输出再释放结点

    void SortPrint(LinkList &head){
    	while(head->next != NULL){
    		pre = head;		//必须维护一个前驱指针
    		p = pre->next;	//p是当前遍历指针
    		while(p->next != NULL){
    			if(p->next->data < pre->next->data){
    				pre = p;	//标记p(最小值)的前驱位置
    				p = p->next;
    			}
    			print(pre->next->data);
    			w = pre->next;		//释放这个替死鬼
    			pre->next = w->next;	
    			free(w);
    		}
    		free(head);	
    	}
    }
    

    10.将题中链表分为序号奇偶两个链表

    思路:设两个指针分别指向新的两个链表

    LinkList DisCreat(LinkList &A){
    	int i = 0;
    	B = (LinkList)malloc(sizeof(LNode));
    	B->next = NULL;		//B表初始化
    	LNode *ra = A,*rb = B;		//分别指向AB两表
    	
    	p = A->next;		//p为工作指针
    	A->next = NULL;
    	
    	while(p != NULL){
    		i++;
    		if(i&2 == 0){			//序号为偶
    			rb->next = p;		//插入rb表
    			rb = p;			//指向新的尾节点
    		}else{					//序号为奇
    			ra->next = p;
    			ra = p;
    		}
    		p = p->next;	//p 往前走
    	}
    	ra->next = NULL;
    	rb->next = NULL;
    	return B;
    }
    

    11.将{a,b,a,b...}拆成{a,a,a..},{b,b,b...}

    思路:与10题思路一样,只不过改成头插法(题目要求)

    LinkList DisCreat_2(LinkList &A){
    	LinkList B = (LinkList)malloc(sizeof(LNode));
    	B->next = NULL;
    	LNode *p = A->next,*q;
    	LNode *ra = A;		//ra始终指向A的尾部
    	
    	while(p != NULL){
    		ra->next = p; 
    		ra = p;			//将指向A的尾部
    		p = p->next;
    		if(p != null) q = p->next;		//用q保存p的后继,否则会断链
    		p->next = B->next;			//头插到B的后面
    		B->next = p;
    		p = q;
    	}
    	ra->next = NULL;		//A的尾部置空
    	return B;
    }
    

    12.删除递增表的重复元素

    思路:工作指针和后继节点相同时则删除

    LinkList DelSame(LinkList &L){
    	LNode *p = L->next,*q;		//p为工作指针
    	
    	if(p == NULL)	return;
    	while(p->next != NULL){
    		q = p->next;	//q始终为p的后继节点
    		if(p->data == q->data){
    			p->next = q->next;
    			free(q);
    		}else{
    			p = p->next;
    		}
    	}
    	
    	
    }
    

    13.将两个递增链表合并为一个递减链表

    思路:同时移动两个指针将较小元素节点的存入链表

    LinkList MergeList(LinkList &La,LinkList &Lb){
    	LNode *r,*pa=La->next,*pb=Lb->next;			//分别指向两个链表
    	La->next = NULL;			//La作为结果链表的头指针
    	
    	while(pa && pb){
    		if(pa->data <= pb->data){
    			r = pa->next;
    			pa->next = La->next;
    			La->next = pa;				//头插逆置  为了维护递减
    			pa = r;					//恢复pa的位置
    		}else{
    			r = pa->next;
    			pb->next = La->next;
    			La->next = pb;				//头插逆置  为了维护递减
    			pb = r;					//恢复pb的位置
    		}
    		if(pa){
    			pb = pa;		//处理非空链表
    		}
    		
    		while(pb){				//依次头插到La中
    			r = pb->next;			
    			pb->next = La->next;
    			La->next = pb;
    			pb = r;
    		}
    		free(Lb);
    	}
    	
    }
    

    14.A、B为递增链表,找到公共部分,产生C链表

    思路:两个指针一起走,谁小谁先走,找到公共节点再继续走

    LinkList GetComm(LinkList A,LinkList B){
    	LNode *p=A->next,*q=B->next,*r,*s			//分别指向两个链表
    	LinkList C = (LinkList)malloc(sizeof(LNode));	
    	r = c;		//r始终指向c的尾巴
    	while(p!=NULL && q!+NULL){				//谁小 谁先走
    		if(p->data < p->data){			
    			p = p->next;
    		}else if(p->data > p->data){
    			q = q->next;
    		}else{
    			s = (LNode*)malloc(sizeof(LNode));
    			s->data = p->data;			//复制p数值
    			r->next = s;		//尾插到C上
    			r = s;
    			p = p->next;			//继续走
    			q = q->next;
    		}
    		r->next = NULL;
    	
    }
    
    

    15.找两个递增的链表的交集

    思路:二路归并(此题考的几率巨大,书上说的...)

    LinkList GetComm(LinkList &la,LinkList &lb){
    	LNode *pa=la->next,pb=lb->next;			//分别指向两个链表
    	pc = la;		//pc一直指向la
    	while(pa && pb){				//谁小 谁先走
    		if(p->data == p->data){			
    			pc->next = pa;
    			pc = pa;
    			pa = pa->next;
    			u = pb;		//替死鬼
    			pb = pb->next;
    			free(u);
    		}else if(pa->data < p->data){
    			u = pa;
    			pa = pa->next;
    			free(u)
    		}else{
    			u = pb;
    			pb = pb->next;
    			free(u)
    		}
    		while(pa){
    			u = pa;
    			pa = pa->next;
    			free(u)
    		}
    		while(pb){
    			u = pb;
    			pb = pb->next;
    			free(u)
    		}
    		free(lb);
    		pc->next = NULL;		//老套路,把新表的路封死
    	
    }
    

    16.判断序列B是不是A的子序列

    思路:B一直重复,A一直往前走,用来找公共的节点

    int Pattern(LinkList A,LinkList B){
    	LNode *p = A;			//p是A的工作指针
    	LNode *pre = A;			//记住每次比较中A链表开始的节点
    	LNode *q = B;
    	
    	while(p && q){
    		if(p->data == q->data){		//节点相同时
    			p = p->next;
    			q = p->next;
    		}else{
    			pre = pre->next;
    			p = pre;			//A新的开始
    			q = B;				//B重复
    		}
    		if(q == NULL){
    			return 1;
    		}else{
    			return 0;
    		}
    }
    

    17.判断是否是 回文链表

    思路:两边来回判断

    int Symmetry(DLinkList L){
    	DNode *p = L->next;			//p是L的往后的工作指针
    	DNode *q = L->prior;			//q是L的往前的工作指针
    	
    	
    	while(q!=q &&p->next!=q){		//节点数为奇或偶的时候
    		if(p->data == q->data){		//节点相同时
    			p = p->next;      //往后走
    			q = p->prior;		//往前走
    		}else{
    			return 0		//比较失败
    		}
    	}
    	return 1;
    }
    
    

    18.将A链接到B链表上,使之仍然为循环链表

    思路:将B链接到A的尾巴上面

    LinkList Link(LinkList &h1,LinkList &h2){
    	LNode *p,*q;
    	p = h1;
    	while(p->next != h1){ 	//寻找h1的尾节点
    		p = p->next;
    	}
    	q = h2;
    	while(q->next != h2){		//寻找h2的尾节点
    		q = q->next;
    	}
    	p->next = h2;		//h2链接到h1之后
    	q->next = h1;		//h2的尾巴指向h1
    	return h1;
    }
    

    19.循环单链表,每次输出并删除最小值节点

    思路:每次循环记录最小值的位置,然后进行删除操作

    LinkList Link(LinkList &L){
    	LNode *p,*pre,*minp,*minpre;
    	while(L->next != L){
    		p = L->next;		//L为工作指针
    		pre = L;		//pre为p的前驱指针
    		minp = p;			//记录最小值的位置
    		minpre = pre;
    		while(p != L){
    			if(p->data < minp->data){	//一直找最小的值
    				minp = p;
    				minpre = pre; 
    			}
    			pre = p;
    			p = p->next;
    		}
    		print("%d",minp->data);		//输出最小值节点元素
    		minpre->next = minp->next;		//删除最小值
    		free(L);		
    	}
    }
    

    21.高效的寻找倒数第K个节点

    思路:设置两个指针,先让p走k步,再p和q一起走,p走到最后,q就是要求的值

    int serch(LinkList list,int k){
    	LNode *p,=list->link,*q=list->link;
    	int count = 0;
    	while(p != NULL){
    		if(count < k){
    			count++;		//前k步先让p先走
    		}else{
    			q = q->link;
    		}	
    		p = p->link;
    	}
    	if(count < k)return 0;	//失败
    	else{
    		printf("%d",p->data);
    		return 1;
    	}
    	
    }
    

    22.找公共节点,并返回公共节点的地址

    思路:与前面的题重复,这里就不做过多赘述

    typedef struct Node{
    	char data;
    	struct Node *next;
    }
    int Len(SNode *head){
    	int len = 0;
    	while(head->next != NULL){
    		len++;
    		head = head->next;
    	}
    	return len;
    }
    
    SNode* findAddr(SNode *str1,SNode *str2){
    	int m n;
    	SNode *p,*q;
    	m = Len(str1);
    	n = Len(str2);
    	
    	for(p=str1;m>n; m--){	//假设m>n,那就先让p先走
    		p = p->next;
    	}
    	for(q=str1;m<n; n--){	//假设m>n,那就先让p先走
    		q = q->next;
    	}
    	while(p->next!=NULL && p->next!=q->next){
    		p = p->next;
    		q = q->next;
    	}
    	return p->next;
    	
    	
    }
    
    

    23.保留第一次出现的节点,删除其他绝对值相等的节点

    思路:题目中给出时间的限制,所以直接空间换时间,使用数组标记

    void func(Pnode h,int n){
    	Pnode p = h,r;
    	int *q,m;
    	q = (int *)malloc(sizeof(int )*(n+1)); //开辟n+1个空间
    	for(int i=0; i<n+1; i++){
    		q[i] = 0;
    	}
    	
    	while(p->link != NULL){
    		//保证m是正数
    		m = p->link->data>0? p->link-data : -p->link->data;
    		if(q[m] == 0){
    			q[m] = 1;		//首次出现,置为1
    			p = p->next;
    		}else{
    			r = p->link;	//替死鬼
    			p->link = r->link;   //跨越式删除
    			free(r);
    		}
    	}
    	free(q);
    }
    

    24.判断链表是否有环,如果有则输出换的入口地址

    思路:使用快慢指针,slow走一步,fast走两步,(证明自行看书)

    Lnode* FindLoop(Lnode *head){
    	Lnode *fast = head,*slow = head;
    	while(slow!=NULL && fast!=NULL){
    		slow = slow->next;			//走一步
    		fast = fast->next->next;	//走两步
    	}
    	if(slow==NULL || fast==NULL){
    		return NULL;
    	}
    	Lnode *p1=head,*p2=slow;
    	while(p1 != p2){
    		p1 = p1->next;
    		p2 = p2->next;
    	}
    	return p1;
    }
    

    25.

    将前面序列变为后面序列

    思路:先找中间节点,将后半段序列逆置,从而后面取一个前面取一个

    void change(node *h){
    	node *p,*q,*r,*s;
    	q = p = h;
    	while(q->next != NULL){  	//找中间节点
    		p = p->next;
    		q = q->next;
    		if(q->next != NULL) 
    			q = q->next;	//走两步
    	}
    	q = p->next;	//p指向中间节点
    	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;		//r指向后半段的下一个节点
    		q->next = s->next;	//将其所指节点插入到s后面
    		s->next = q;		//s指向前半段的下一个插入点
    		s = q->next;
    		q = r;
    	}
    }
    

    链表完美撒花

  • 相关阅读:
    【贪心】【堆】Gym
    【并查集】Gym
    【拓扑排序】【bitset】Gym
    【递归】【线段树】【堆】AtCoder Regular Contest 080 E
    【二分图】【并查集】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem L. Canonical duel
    【动态规划】【滚动数组】【bitset】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem J. Terminal
    【二分】【字符串哈希】【二分图最大匹配】【最大流】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem I. Minimum Prefix
    【枚举】【最小表示法】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem F. Matrix Game
    【推导】【构造】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem E. Space Tourists
    【推导】【贪心】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem D. Clones and Treasures
  • 原文地址:https://www.cnblogs.com/xiaofff/p/13049613.html
Copyright © 2011-2022 走看看