1. 程式人生 > >渣渣渣變渣渣系列(7)

渣渣渣變渣渣系列(7)

一、題目描述:


二、演算法思想:

考慮到遍歷連結串列的時間開銷比較大,題目中僅要求實現最大的時間效率,所以可以採用用空間換時間的方法,具體操作就是用一個數組儲存已經出現的數字,這樣只需要遍歷一次連結串列即可。

三、核心程式碼:

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)