鏈表--單鏈表的創建與查找


1、鏈接存儲方法
     鏈接方式存儲的線性表簡稱為鏈表(Linked List)。
     鏈表的具體存儲表示為:
  ① 用一組任意的存儲單元來存放線性表的結點(這組存儲單元既可以是連續的,也可以是不連續的)
  ② 鏈表中結點的邏輯次序和物理次序不一定相同。為了能正確表示結點間的邏輯關系,在存儲每個結點值的同時,還必須存儲指示其后繼結點的地址(或位置)信息(稱為指針(pointer)或鏈(link))
注意:
  
鏈式存儲是最常用的存儲方式之一,它不僅可用來表示線性表,而且可用來表示各種非線性的數據結構。

2、鏈表的結點結構
  ┌──┬──┐
  │data│next│
  └──┴──┘ 
       data域--存放結點值的數據域
       next域--存放結點的直接后繼的地址(位置)的指針域(鏈域)
注意:
     ①鏈表通過每個結點的鏈域將線性表的n個結點按其邏輯順序鏈接在一起的。
     ②每個結點只有一個鏈域的鏈表稱為單鏈表(Single Linked List)。
【例】線性表(bat,cat,eat,fat,hat,jat,lat,mat)的單鏈表示如示意圖

     

3、頭指針head和終端結點指針域的表示
     單鏈表中每個結點的存儲地址是存放在其前趨結點next域中,而開始結點無前趨,故應設頭指針head指向開始結點。
注意:
     
鏈表由頭指針唯一確定,單鏈表可以用頭指針的名字來命名。
【例】頭指針名是head的鏈表可稱為表head。
  終端結點無后繼,故終端結點的指針域為空,即NULL。

4、單鏈表的一般圖示法
     由於我們常常只注重結點間的邏輯順序,不關心每個結點的實際位置,可以用箭頭來表示鏈域中的指針,線性表(bat,cat,fat,hat,jat,lat,mat)的單鏈表就可以表示為下圖形式。

       
5、單鏈表類型描述
  typedef char DataType; //假設結點的數據域類型為字符
  typedef struct node{   //結點類型定義
       DataType data;    //結點的數據域
       struct node *next;//結點的指針域
     }ListNode;
  typedef ListNode *LinkList;
  ListNode *p;
  LinkList head;
  注意:
     ①LinkList和ListNode *是不同名字的同一個指針類型(命名的不同是為了概念上更明確)
     ②LinkList類型的指針變量head表示它是單鏈表的頭指針
     ③ListNode *類型的指針變量p表示它是指向某一結點的指針

6、指針變量和結點變量

┌────┬────────────┬─────────────┐ 
│    │    指針變量    │     結點變量      │
├────┼────────────┼─────────────┤
│  定義  │在變量說明部分顯式定義  │在程序執行時,通過標准    │
│        │                        │函數malloc生成            │
├────┼────────────┼─────────────┤
│  取值  │ 非空時,存放某類型結點 │實際存放結點各域內容      │
│        │的地址                  │                          │
├────┼────────────┼─────────────┤
│操作方式│ 通過指針變量名訪問     │ 通過指針生成、訪問和釋放 │
└────┴────────────┴─────────────┘
   
①生成結點變量的標准函數
     p=( ListNode *)malloc(sizeof(ListNode));
//函數malloc分配一個類型為ListNode的結點變量的空間,並將其首地址放入指針變量p中
②釋放結點變量空間的標准函數 
     free(p);//釋放p所指的結點變量空間
③結點分量的訪問 
     
利用結點變量的名字*p訪問結點分量
 方法一:(*p).data和(*p).next
 方法二:p-﹥data和p-﹥next
④指針變量p和結點變量*p的關系 
 
    指針變量p的值——結點地址
     結點變量*p的值——結點內容
     (*p).data的值——p指針所指結點的data域的值
     (*p).next的值——*p后繼結點的地址
  *((*p).next)——*p后繼結點
注意:
     ① 若指針變量p的值為空(NULL),則它不指向任何結點。此時,若通過*p來訪問結點就意味着訪問一個不存在的變量,從而引起程序的錯誤。
     ② 有關指針類型的意義和說明方式的詳細解釋。

 

(1) 頭插法建表
① 算法思路
     從一個空表開始,重復讀入數據,生成新結點,將讀入數據存放在新結點的數據域中,然后將新結點插入到當前鏈表的表頭上,直到讀入結束標志為止。
     

     具體方法【參見動畫演示
注意:
     該方法生成的鏈表的結點次序與輸入順序相反。

② 具體算法實現
    LinkList CreatListF(void)
      {//返回單鏈表的頭指針
          char ch;
          LinkList head;//頭指針
          ListNode *s;  //工作指針
          head=NULL;    //鏈表開始為空
          ch=getchar(); //讀入第1個字符
          while(ch!='\n'){
              s=(ListNode *)malloc(sizeof(ListNode));//生成新結點
              s->data=ch;   //將讀入的數據放入新結點的數據域中
              s->next=head;
              head=s;
              ch=getchar();  //讀入下一字符
            }
          return head;
       } 

 

(2) 尾插法建表 
① 算法思路 
     
從一個空表開始,重復讀入數據,生成新結點,將讀入數據存放在新結點的數據域中,然后將新結點插入到當前鏈表的表尾上,直到讀入結束標志為止。

      
     具體方法【參見動畫演示
注意:
    ⒈采用尾插法建表,生成的鏈表中結點的次序和輸入順序一致
    ⒉必須增加一個尾指針r,使其始終指向當前鏈表的尾結點

② 具體算法實現 
    
LinkList CreatListR(void)
      {//返回單鏈表的頭指針
          char ch;
          LinkList head;//頭指針
          ListNode *s,*r;  //工作指針
          head=NULL;    //鏈表開始為空
          r=NULL;//尾指針初值為空
          ch=getchar(); //讀入第1個字符
          while(ch!='\n'){
              s=(ListNode *)malloc(sizeof(ListNode));//生成新結點
              s->data=ch;   //將讀入的數據放入新結點的數據域中
           if (head!=NULL)
               head=s;//新結點插入空表
           else
               r->next=s;//將新結點插到*r之后
              r=s;//尾指針指向新表尾
           ch=getchar();  //讀入下一字符
         }//endwhile
        if (r!=NULL)
             r->next=NULL;//對於非空表,將尾結點指針域置空head=s;
         return head;
    } 
注意:
   
⒈開始結點插入的特殊處理
       由於開始結點的位置是存放在頭指針(指針變量)中,而其余結點的位置是在其前趨結點的指針域中,插入開始結點時要將頭指針指向開始結點。
   ⒉空表和非空表的不同處理
      若讀入的第一個字符就是結束標志符,則鏈表head是空表,尾指針r亦為空,結點*r不存在;否則鏈表head非空,最后一個尾結點*r是終端結點,應將其指針域置空。

 

 

 

2.單鏈表的查找運算 
(1)按序號查找
① 鏈表不是隨機存取結構
     在鏈表中,即使知道被訪問結點的序號i,也不能像順序表中那樣直接按序號i訪問結點,而只能從鏈表的頭指針出發,順鏈域next逐個結點往下搜索,直至搜索到第i個結點為止。因此,鏈表不是隨機存取結構。

② 查找的思想方法
     計數器j置為0后,掃描指針p指針從鏈表的頭結點開始順着鏈掃描。當p掃描下一個結點時,計數器j相應地加1。當j=i時,指針p所指的結點就是要找的第i個結點。而當p指針指為null且j≠i時,則表示找不到第i個結點。
注意:
     頭結點可看做是第0個結點。

③具體算法實現
 
    ListNode* GetNode(LinkList head,int i)
     {//在帶頭結點的單鏈表head中查找第i個結點,若找到(0≤i≤n),
      //則返回該結點的存儲位置,否則返回NULL。
      int j;
      ListNode *p;
      p=head;j=0;//從頭結點開始掃描
      while(p->next&&j<i){//順指針向后掃描,直到p->next為NULL或i=j為止
          p=p->next;
          j++;
        }
      if(i==j)
         return p;//找到了第i個結點
      else return NULL;//當i<0或i>0時,找不到第i個結點
     } 

④算法分析

     算法中,while語句的終止條件是搜索到表尾或者滿足j≥i,其頻度最多為i,它和被尋找的位置有關。在等概率假設下,平均時間復雜度為:

          


(2) 按值查找
①思想方法 
    
 從開始結點出發,順着鏈逐個將結點的值和給定值key作比較,若有結點的值與key相等,則返回首次找到的其值為key的結點的存儲位置;否則返回NULL。

②具體算法實現

    ListNode* LocateNode (LinkList head,DataType key)
      {//在帶頭結點的單鏈表head中查找其值為key的結點
        ListNode *p=head->next;//從開始結點比較。表非空,p初始值指向開始結點
        while(p&&p->data!=key)//直到p為NULL或p->data為key為止
             p=p->next;//掃描下一結點
         return p;//若p=NULL,則查找失敗,否則p指向值為key的結點
       }

③算法分析

     該算法的執行時間亦與輸入實例中key的取值相關,其平均時間復雜度分析類似於按序號查找,為O(n)。

 

附源代碼

#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
char data;
struct node *next;
}ListNode;
typedef ListNode * LinkList;

//頭插入法
LinkList creatListF(void)
{
LinkList head=NULL;
ListNode *s;
char ch;
ch=getchar();

while(ch!='\n')
{
s=(ListNode *)malloc(sizeof(ListNode));
if(s==NULL)
{
return NULL;
}
s->data=ch;
s->next=head;
head=s;//head要不斷向前移動,一直指着最前線,這樣存儲的數據與輸入的數據順序相反
ch=getchar();
}

return head;
}

//尾插入法
LinkList creatListR(void)
{
LinkList head=NULL,r=NULL;
ListNode *s;
char ch;
ch=getchar();

while(ch!='\n')
{
s=(ListNode *)malloc(sizeof(ListNode));

s->data=ch;
if(head==NULL)
head=s;//新結點插入空表
else
r->next=s;//如果是頭結點,不執行這一步,頭結點特別處理

r=s;//一直指向最后一個,無論點,r肯定是指向最后一個
if(r!=NULL)
r->next=NULL;//對於非空表,將尾結點指針域置空,防止內存讀寫錯誤

ch=getchar();
}

return head;
}

//根據查找第i個結點
ListNode * getNodebyi(LinkList head,int i)
{
LinkList p=head;
int j=0;
while(p->next && j<i)//如果p->next為NULL,那么沒有后結點,關鍵判斷后繼結點
{
p=p->next;
j++;
}
//循環結束后,要不p為NULL,要不j=i
if(j==i)
{
return p;
}
else
return NULL;
}
ListNode * getNodebykey(LinkList head,char key)
{
LinkList p=head;//本程序是不帶頭結點的
while(p && key!=p->data)//直到p為NULL或p->data為key為止
{
p=p->next;
}
return p;//若p=NULL,則查找失敗,否則p指向值為key的結點

}

void print(LinkList head)
{

while(head)
{
printf("%c ",head->data);
head=head->next;//在創建時,要將head設置為NULL,否則內存出錯;LinkList head這樣聲明只是指向一個未知的值

}
}
int main()
{
//LinkList head=creatListF();
LinkList head=creatListR();
print(head);
//LinkList node=getNode(head,2);
//printf("\n查找第2個\n");
LinkList node=getNodebykey(head,'c');
printf("\n查找第2個\n");
printf("%c",node->data);
getchar();
return 0;
}


免責聲明!

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



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