渣渣渣變渣渣系列(7)
阿新 • • 發佈:2018-12-30
一、題目描述:
二、演算法思想:
考慮到遍歷連結串列的時間開銷比較大,題目中僅要求實現最大的時間效率,所以可以採用用空間換時間的方法,具體操作就是用一個數組儲存已經出現的數字,這樣只需要遍歷一次連結串列即可。
三、核心程式碼:
struct Link*rmv_cf(struct Link*str1,int n) { int i; int j=1; int A[n+1];//定義一個數組用來儲存所有可能的值 int p;//用來標誌是否在記錄陣列中命中記錄 Link*start=str1;//儲存連結串列指標 Link*tmp=NULL;//用來暫存要刪除的節點指標 A[0]=str1->data;//第一個值不可能重複,直接加入A[]。 while(str1->link!=NULL)//遍歷連結串列,同時對每個節點在A[]中查詢,當前指標不是指向最後節點則迴圈 { //如果存在則刪除節點,不存在則將值加入A[]。 p=0;//每次迴圈p更新為0 for(i=0;i<n;i++) { if(str1->link->data==A[i])//在A[]中查詢到值,刪除節點。 { tmp=str1->link;//暫存要刪除的節點指標 str1->link=str1->link->link;//將指標往後移動 p=1;//將標誌置為1,表示命中 free(tmp);//釋放該節點所佔記憶體 break;//停止在A[]中是匹配 } } if(p==0)//如果未在A[]中查詢到值, { A[j]=str1->link->data;//則將值新增到A[] // printf("A[%d]=%d\n",j,A[j]); j++; //將指標往後移動 if(str1->link!=NULL)//確定當前指標不是指向最後節點,可以移動 str1=str1->link;//連結串列指標往後移動 else//當前指標已經指向最後一個節點,無法往後移動,也無需迴圈,直接退出 break;//細心的親可能會問,為啥不判斷最後一個節點呢? //其實我們在匹配的時候是使用if(str1->link->data==A[i]), //不是使用str1->data,也就說當指標指向倒數第二個節點時 //就在判斷最後一個節點的資料是否在A[]中匹配,為啥要這樣? //因為我們這是單鏈表,如果我們使用str1->data判斷當前節點, //是無法通過改變指標,直接實現刪除的,因為當前的節點的前驅 //無法直接讀取,必須重新遍歷。 } } return start ; }
四、完整程式碼:
#include<stdio.h> #include<stdlib.h> int listlen(struct Link*head);/*求連結串列長度函式*/ struct Link*rmv_cf(struct Link*str1,int n);/*找到共同字尾的起始地址*/ struct Link*AppendNode(struct Link*head,int c);/*在連結串列末尾新增一個節點*/ void DeleteMemory(struct Link*head);/*刪除動態分配的記憶體空間*/ void DisplyLink(struct Link*head);/*遍歷連結串列*/ /*定義連結串列節點*/ struct Link{ char data; struct Link*link; }; int main() { int n; int i; int c; struct Link*str1=NULL;//定義str1的頭節點指標 //建立連結串列str1 printf("please input the number of the node: "); scanf("%d",&n); printf("Please input the data:"); for(i=0;i<n;i++) { scanf("%d",&c); str1=AppendNode(str1,c);//向head為頭指標的連結串列末尾新增節點 } printf("the List is:"); DisplyLink(str1);//列印初始連結串列 printf("After rmv_cp:"); DisplyLink(rmv_cf(str1,n));//列印刪去重複元素的連結串列 //釋放連結串列所佔的記憶體 DeleteMemory(str1); return 0; } /*求連結串列長度函式*/ int listlen(struct Link*head){ int len=0; while(head->link!=NULL){ len++; head=head->link; } return len; } /*刪除連結串列中的重複元素*/ struct Link*rmv_cf(struct Link*str1,int n) { int i; int j=1; int A[n+1];//定義一個數組用來儲存所有可能的值 int p;//用來標誌是否在記錄陣列中命中記錄 Link*start=str1;//儲存連結串列指標 Link*tmp=NULL;//用來暫存要刪除的節點指標 A[0]=str1->data;//第一個值不可能重複,直接加入A[]。 while(str1->link!=NULL)//遍歷連結串列,同時對每個節點在A[]中查詢,當前指標不是指向最後節點則迴圈 { //如果存在則刪除節點,不存在則將值加入A[]。 p=0;//每次迴圈p更新為0 for(i=0;i<n;i++) { if(str1->link->data==A[i])//在A[]中查詢到值,刪除節點。 { tmp=str1->link;//暫存要刪除的節點指標 str1->link=str1->link->link;//將指標往後移動 p=1;//將標誌置為1,表示命中 free(tmp);//釋放該節點所佔記憶體 break;//停止在A[]中是匹配 } } if(p==0)//如果未在A[]中查詢到值, { A[j]=str1->link->data;//則將值新增到A[] // printf("A[%d]=%d\n",j,A[j]); j++; //將指標往後移動 if(str1->link!=NULL)//確定當前指標不是指向最後節點,可以移動 str1=str1->link;//連結串列指標往後移動 else//當前指標已經指向最後一個節點,無法往後移動,也無需迴圈,直接退出 break;//細心的親可能會問,為啥不判斷最後一個節點呢? //其實我們在匹配的時候是使用if(str1->link->data==A[i]), //不是使用str1->data,也就說當指標指向倒數第二個節點時 //就在判斷最後一個節點的資料是否在A[]中匹配,為啥要這樣? //因為我們這是單鏈表,如果我們使用str1->data判斷當前節點, //是無法通過改變指標,直接實現刪除的,因為當前的節點的前驅 //無法直接讀取,必須重新遍歷,這是實現這個演算法的關鍵細節。 } } return start ; } //函式功能:新建一個節點並新增到連結串列末尾,返回新增節點後的表頭指標 struct Link*AppendNode(struct Link*head,int data) { struct Link*p=NULL; struct Link*pr=head; p=(struct Link*)malloc(sizeof(struct Link)); if(p==NULL) { printf("no enough memory to allocate!\n"); exit(0); } if(head==NULL) { head=p; } else { while(pr->link!=NULL) { pr=pr->link; } pr->link=p; } p->data=data; p->link=NULL; return head; } //釋放head指向連結串列中的所有節點所佔用的記憶體 void DeleteMemory(struct Link*head) { struct Link*p=head; struct Link*pr=NULL; while(p!=NULL) { pr=p; p=p->link; free(pr); } } //顯示連結串列中所有節點的節點好和該節點中的資料項內容 void DisplyLink(struct Link*head) { struct Link*p=head; int j=1; while(p!=NULL) { printf("->[%d--%d] ",j,p->data); p=p->link; j++; } printf("\n"); }
五、測試分析:
時間複雜度O(nlgn),空間複雜度O(n)