我會盡量把每一步都說明清楚,每一行代碼所表示的含義,以及會用直觀的代碼講明白。
鏈表:是一種常見且重要的動態存儲分布的數據結構,它由若干個同一結構體類型的“節點”組成,每一個節點含有存儲數據的信息以及存放指向下一個節點的指針,我們稱之為next指針,最后一個單元的Next指針指向NULL
鏈表的常用操作包括建立鏈表,鏈表的遍歷,插入節點,刪除節點,和查找等等。
下面是結點的結構圖
鏈表的節點是用結構體類型的遞歸來定義的,一個單向鏈表節點的類型定義如下:
struct stu_node { int data; /*數據域*/ struct stu_node *next; //指向下一個結點的指針 };
為了方便理解我就不用下面的這種方式了
struct stu_node { int data;
struct stu_node *next; }stu_node,*Linklist;
單向鏈表的示意圖:
單向鏈表的示意圖
鏈表中有一個head頭指針變量,頭結點是為了處理空表的方便所引用的,用來存放鏈表中的第一節點的位置,它的data域不存放任何信息
鏈表的創建之前先假定定鏈表節點的類型定義為一個學生鏈表,定義如下
#include <stdio.h> #include <stdlib.h>
struct stu_node { int num ; char naem[20]; int score; struct stu_node *next; };
1.鏈表的建立:建立鏈表就是一個一個的輸入各節點的數據,然后建立各節點之間的勾鏈關系
單向鏈表的建立有“插表頭”,“插表尾”兩種方法,插表頭是將新節點作為新的表頭插入鏈表,插表尾是將新節點作為表尾鏈接到鏈表的表尾,無論使用哪種方法,首先要建立一個空表,然后在此空表的表頭或表尾插入新節點。
插表頭建立 單向鏈表
插表頭的算法如下:
① head=NULL; //頭指針指向空,表示空鏈表
②指針p指向一個產生的新節點 ③p->next=head;head=p; //插表頭操作(新節點的next指針指向原來的頭指針head。head再指向p作為頭指針)
④循環執行②③,可繼續建立新節點
插表尾
插表尾建立單向鏈表,需要定義一個指針last一直指向表尾,指針p指向新的節點,將新節點直接鏈在表尾(last->next=p)
插表尾算法的描述如下:
①head=last=NUll //建立空表。last是表尾指針 ②指針p指向一個產生的一個新節點p->next=NULL; //新節點作表尾 ③如果head為NULL則 head=p; //在空表中插入第一個節點
否則last->next=p; //插表尾操作 ④last=p //表尾指針last指向新的表尾節點 ⑤循環執行②③④,可繼續建立新節點
下面將采用插表尾的方法創建一個學生成績信息的單向鏈表
#include<stdio.h> #include<stdlib.h>
struct stu_node { int num; char name[20]; int score; struct stu_node *next; } ; int n; //n表示鏈表的長度
struct stu_node * create_stu(){ struct stu_node *head,*last,*p; head=last=NULL; //建立空表
n=0; printf("輸入學號姓名成績(學號為0時停止輸入)"); do { p=(struct stu_node *)malloc(sizeof(struct stu_node)); scanf("%d%s%d",&p->num,p->name,&p->score); p->next=NULL; //新節點作為表尾
if(p->num==0)break; //學號為0時停止循環
else if(head==NULL)head=p; //當表為空表時,直接加入新節點
else last->next=p; //新節點插入表尾
last=p; //表尾指針指向新的表尾節點p
n++; //表長加一
} while(1); free(p); //釋放指針p
return head ; //返回頭指針
}
鏈表的遍歷:鏈表的遍歷就是逐個掃描鏈表的節點,然后顯示節點的信息,從頭指針開始依次通過及節點的next成員訪問后繼節點(p=p->next)直到表尾
先將指針p指向head, 再通過p=p->next訪問后一個節點,直到表尾。
以下是代碼
void print_stu(struct stu_node *head) { struct stu_node *p; p=head; if(head==NULL){ printf("空表"); return; } printf("學號 姓名 成績"); do{ printf("%2d %s%d\n",p->num,p->name,p->score); p=p->next; //指針指向下一個節點
} while(p!=NULL) }
鏈表的插入
代碼如下
/*
插入指定節點的后面(此例是指定學號的節點)
*/
struct stu_node *insert(struct stu_node *head, int num) { struct stu_node *p; p=head; // 指針p指向第一個節點
struct stu_node *s=(struct stu_node *)malloc(struct stu_node); // 待插入的結點s
if(head==NULL) { head=s;s->next=NULL; //當鏈表為空時,新節點作為頭結點插入
n+=1; 鏈表節點長度加1 return head; } else { while(p->num!=num&&p->next!=NULL)//p指向的結點不是所查找的,並且不是最后一個結點,繼續往下找
{ p=p->next; if(p->num=num){ /*找到了想要插入的位置*/ s->next=p->next;
p->next=s;
n+=1; //節點總數加一 }
return head; } }
如果不好理解的話再換個方法吧 下面用兩個指針來插入,操作如下圖
下面是代碼
/* 假設有一個學生成績信息的單向鏈表,插入學號為num的節點 */
struct stu_node *insert(struct stu_node * head,struct stu_node * s) { //s 是將要插入的節點
struct stu_node *p,*q; p=head; //指針p 指向第一個節點
if(head==NULL) { head=s;s->next=NULL; //鏈表為空時,新節點作為頭節點插入
} else{ while((s->num!=p->num)&&(p->next!=NULL)) { q=p;p=p->next; //移動pq指針 尋找插入點
} if(s->num==p->num){ //找到插入的節點后在指針p與q之間插入新節點
if(p==head)head=s; //新節點作為頭節點插入
else q->next=s; //新節點插入q指向的節點之后
s->next=p; } else{ //新節點作為尾節點插入
p->next=s; s->next=NULL; } } n++; //鏈表節點長度加一
return head; }
鏈表的刪除
刪除應該很好理解:就是利用指針s從第一個節點開始找,指針p總是指向指針s所指節點的前驅。當指針s指向的節點就是待刪除的節點時直接修改其前驅節點的成員值(p->next=s->next;),直接改變勾鏈關系,就可以實現節點刪除
刪除算法詳細如下
①指針s指向表頭s=head;
②當s指向的不是待刪除的節點且還沒有到表尾時,移動指針p=s;s=s->next;
③當找到刪除節點時:
如果s==head,則:p=head;head=head->next; /* 當刪除的節點是表頭節點時*/
否則p->next=s->next; /*刪除s所指向的節點*/
④free(s); /*釋放刪除節點的內存*/
//假設有一個學生成績的單向鏈表,編寫一個函數實現 刪除學號為num的節點
struct stu_node *delete_stu(struct stu_node* head,int num)
{
struct stu_node *p,*s;
if(head==NULL) /* 當鏈表為空時*/
{
printf("鏈表為空");
}
s=head; /*指針s指向第一個節點,從表頭開始查找*/
while(num!=s->num&&s->next!=NULL) /*查找的節點不是想要的且還沒到達表尾*/
{
p=s;s=s->next; /*指針后移*/
}
if(num==s->num){ /*找到刪除的節點*/
if(s==head)head=s->next; /*當刪除的是頭結點時*/
else p->next=s->next; /*當刪除的節點不是頭節點時*/
printf("已經刪除:%d\n",num);
free(s); /*釋放刪除節點的內存*/
n--; /*節點長度減一*/
}
else printf("%d 表中無此點",num); /*當找不到符合的節點時*/
return head;
}
/*delete_stu()函數的形參num接收的是想刪除的學號,函數返回值是頭指針*/
emmmm接下來是關於鏈表的查找
鏈表的查找也是分為兩種①按照序號查找②按值查找
①鏈表不是隨機存取結構在鏈表中,即使知道被訪問結點的序號i,也不能像順序表中那樣直接按序號i訪問結點,而只能從鏈表的頭指針出發,順鏈域next逐個結點往下搜索,直至搜索到第i個結點為止。因此,鏈表不是隨機存取結構。
頭節點看做第0個節點,下面給的例子意思是 查找節點值為num的節點信息
/*鏈表的查詢,查找特定節點的信息*/
struct stu_node *find_stu(struct stu_node* head,int num) { struct stu_node *p=head; int j=0; while(p->next&&j<num) { p=p->next; j++; } if(j==num) { printf("查找的節點為%d 學號 姓名 信息\n"); printf("%d\t%s\t%d\n",p->num,p->name,p->score); return p; } else
return NULL; }
②按值查找:從頭節點出發依次逐個將節點的數據域和定值num比較(這里的num代表的指定學生的學號)若節點的數據域與定值num相同,打印該節點的信息,且返回該節點,若找不到匹配節點的數據域則返回NULL
下面是實現按值查找的算法
/*查詢特定學號的學生的信息*/
struct stu_node *look_stu(struct stu_node *head,int num) { struct stu_node *p=head; while(p&&num!=p->num) { p=p->next; } printf("%2d\t%s\t%2d\n",p->num,p->name,p->score); return p; }
emmm關於鏈表的基本操作的介紹與操作已經介紹完下面我們進行測試;
測試代碼如下
main() { struct stu_node* head=NULL,*p; int choise,num; do { printf("請選擇(0-4)"); printf("1.建立鏈表\t2.插入\t3.刪除\t4.遍歷\t5.查找\t0.退出\n"); scanf("%d",&choise); switch(choise) { case 1: head=create_stu(); break; case 2: printf("輸入待插入的學生的信息:"); p=(struct stu_node *)malloc(sizeof(struct stu_node)); scanf("%d%s%d",&p->num,p->name,&p->score); head=insert_stu(head,p); break; case 3:printf("刪除的學生學號"); scanf("%d",&num); head=delete_stu(head,num); break; case 4:print_stu(head); case 5:printf("輸入想要查找的節點序號"); scanf("%d",&num); head=look_stu(head,num); break; case 0: break; } }while(choise!=0); }
下面是測試的結果:
最后也是最重要的:謝謝你看我的博客 (如果有人看我的博客的話),如果有不足之處的話還請指出,后續有時間的話我會考慮增加新的功能的。 禁止轉載