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;
}
}
鏈表完美撒花
