⚡王道課后題之鏈表⚡


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 *pre = NULL, *cur = head;
        while (cur != NULL) {
            ListNode *t = cur->next;         //再存cur的下一個指針          
            cur->next = pre;                  //cur指向pre
            pre = cur;                        //cur往前走一步
            cur = t;                        //pre往前走一步
        }
        return pre;
    }
    

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;
	}
}

鏈表完美撒花


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM