重學資料結構(一)單鏈表
阿新 • • 發佈:2018-11-08
最近在重新學習資料結構,特此記錄學習過程,碼農再次上線
關於單鏈表的一些基本操作,以下為基本思路程式碼
首先看一張直觀圖
連結串列的結構體定義如下(為簡便,這裡的ElemType採用int):
//單鏈表
typedef struct LNode {
int data;
struct LNode *next;
} LinkList;
接下來肯定就是建立連結串列,而目前建立單鏈表分為兩種方式,分別是"頭插法"與"尾插法",首先看圖示:
清楚明白:
- 頭插法就是每次插入的節點在上一個節點之前,頭節點之後
- 尾插法就是每次插入的節點在上一節點之後
我們對比來看節點的插入:
//頭插法核心程式碼
s = (LinkList *) malloc(sizeof(LinkList));
s->data = a[i];
s->next = L->next;//將s插入到下一節點之前
L->next = s;
//尾插法核心程式碼
s = (LinkList *) malloc(sizeof(LinkList));
s->data = a[i];
r->next = s;//將s插入到r之後
r = s;
那麼寫成函式,就是如下所示:
/**
* 頭插法建立單鏈表
* @param a 陣列元素,對應data
* @param n 連結串列長度(除去頭結點)
*/
void CreateListByHead(LinkList *L, const int a[], int n) {
LinkList *s;
//建立頭節點
L->next = NULL;
for (int i = 0; i < n; ++i) {
s = (LinkList *) malloc(sizeof(LinkList));
s->data = a[i];
s->next = L->next;//將s插入到下一節點之前
L->next = s;
}
}
/**
* 尾插法建立單鏈表
* @param a 陣列元素,對應data
* @param n 連結串列長度(除去頭結點)
*/
void CreateListByTail(LinkList *L, const int a[], int n) {
LinkList *r = L, *s;
for (int i = 0; i < n; ++i) {//迴圈建立資料節點
s = (LinkList *) malloc(sizeof(LinkList));
s->data = a[i];
r->next = s;//將s插入到r之後
r = s;
}
r->next = NULL; //尾節點next域置為空
}
那麼接下來的集中基本操作方法也就不再贅述,其實都是一些對連結串列的操作,指標的移動,臨界條件的判斷,我也都寫在註釋中了,尤其是第一個Delete方法寫的最詳細,可以仔細看一下哦,為了直觀一點,我也附上程式碼圖片(編譯環境Clion)~
按照邏輯序號刪除某節點
這裡也要注意是否會斷鏈的問題,free()不要釋放錯了,否則會有錯誤
/**
* 刪除單鏈表中的第i個元素
* @param L 連結串列
* @param i 第i個元素
* @return 程式執行完畢狀態
*/
int ListDelete(LinkList *L, int i) {
int j = 0;
LinkList *p = L, *q;
/*
*找到第i-1個節點 ,p!= NULL 是防止尋找的節點超過連結串列,
* 比如一共有四個節點,而i=6即想找到第6個節點,那麼當j++變成5,
* p指向第五個不存在的節點後就會返回NULL
*此時迴圈結束,p == NULL
*/
while (j < i - 1 && p != NULL) {
j++;
p = p->next;
}
if (p == NULL)
return ERROR;
else {
q = p->next;
/*
* 如果p->next為空,則返回ERROR
* 此時是為了防止邊界條件的情況
* 若連結串列一共有四個節點,而我們要找到第五個節點,
* 那麼在之前的找到的i-1也就是第四個節點
* 此時q = p->next 返回的就是一個NULL值
*/
if (q == NULL)
return ERROR;
p->next = q->next;
free(q);
return TRUE;
}
}
在邏輯位置之後插入節點
這裡注意,插入操作遵循***先右後左***原則,否則會斷鏈,如圖:
/**
* 在邏輯位置後插入節點
* @param L 傳入連結串列
* @param i 邏輯位置
* @param e 插入資料
* @return 插入成功返回1,失敗返回0
*/
int ListInsert(LinkList *L, int i, int e) {
int j = 0;
LinkList *p = L, *s;
while (j < i && p != NULL) {//查詢第i個位置
j++;
p = p->next;
}
if (p == NULL)//如果為空返回錯誤值
return ERROR;
else {
s = (LinkList *) malloc(sizeof(LinkList));
s->data = e;
s->next = p->next;
p->next = s;
return TRUE;
}
}
按照元素值查詢特定節點
這裡預設連結串列中的元素是唯一的,如果想要查詢不唯一的數值,就需要大家去拓展了,也不是很難,可以採用將查到的資料邏輯序號放到陣列中返回等等等等方法~
/**
* 按照元素值查詢特定節點並返回邏輯序號
* @param L 傳入單鏈表
* @param e 傳入特定元素
* @return 沒有找到返回0,找到返回邏輯序號i
*/
int FindElement(LinkList *L, int e){
int i = 1; //不要忘了初始值!
LinkList *s = L; //指向開始節點
while (s != NULL && s->data != e){
s = s->next;
i++;
}
if (s == NULL )
return ERROR;
else
return i;
}
輸出連結串列
/**
* 輸出單鏈表
*/
void DispList(LinkList *L) {
while (L->next != NULL) {
printf("%d->", L->data);
L = L->next;
}
printf("%d", L->data);
}
最後附上測試用的main函式,為了方便就直接用陣列賦值了
#include <stdio.h>
#include "linklist.h"
int main() {
int a[10];
for (int j = 0; j < 10; ++j) a[j] = j + 1;
LinkList *L = (LinkList *) malloc(sizeof(LinkList));
CreateListByTail(L, a, 10);
printf("建立完成後的單鏈表為: ");
DispList(L);
ListInsert(L, 5, 555);
printf("\n在第五個節點後插入555之後的單鏈表為: ");
DispList(L);
ListDelete(L, 3);
printf("\n刪除第三個節點後的單鏈表為: ");
DispList(L);
int i = FindElement(L, 5);
if (i != 0)
printf("\n擁有此元素!在第%d個位置上!",i);
else
printf("\n沒有此元素!!!!");
return 0;
}