鄂州做網(wǎng)站報價谷歌搜索引擎免費入口鏡像
目錄
- 1. 鏈表的概念及結構
- 2. 實現(xiàn)單鏈表
- 初始化
- 尾插
- 頭插
- 尾刪
- 頭刪
- 查找
- 在指定位置之前插入數(shù)據(jù)
- 在指定位置之后插入數(shù)據(jù)
- 刪除指定位之前的節(jié)點
- 刪除指定位置之后pos節(jié)點
- 銷毀鏈表
- 3. 完整代碼
- test.c
- SList.h
- 4. 鏈表的分類
1. 鏈表的概念及結構
在順序表中存在一定的問題:
- 順序表的中間/頭部插入、刪除需要挪動數(shù)據(jù)
- 擴容存在性能的消耗
- 會有空間的浪費
而鏈表是獨立的節(jié)點組成
鏈表概念:鏈表是一種物理存儲結構上非連續(xù)、非連續(xù)的存儲結構,數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序實現(xiàn)的。
鏈表、順序表都是線性表
邏輯結構一定是線性的
物理結構不一定是線性的
圖中指針變量plist保存的是第一個節(jié)點的地址,我們稱plist此時“指向”第一個節(jié)點,如果我們希望plist“指向第二個節(jié)點時,只需要修改plist保存的內(nèi)容為0x0012FFA0
為什么還需要指針變量來保存下一個節(jié)點的位置?
鏈表中每個節(jié)點都是獨立申請的(即需要插入數(shù)據(jù)時才去申請一塊節(jié)點的空間),我們需要通過指針變量來保存下一個節(jié)點位置才能從當前節(jié)點找到下一個節(jié)點。
鏈表是由一個一個的節(jié)點組成的
一個節(jié)點由兩部分組成:要存儲的數(shù)據(jù)+指針
int a = 1;
int* pa = &a;
節(jié)點結構的定義
struct SListNode{
int data;
struct SListNode* next;
}
鏈表結構體的打印方式:
補充說明:
- 鏈式機構在邏輯上是連續(xù)的,在物理結構上不一定連續(xù)
- 節(jié)點一般是從堆上申請的
- 從堆上申請來的空間,是按照一定策略分配出來的,每次申請的空間可能連續(xù),可能不連續(xù)
2. 實現(xiàn)單鏈表
初始化
typedef int SLTDataType;
//鏈表是由節(jié)點組成的
typedef struct SListNode
{SLTDataType data;//節(jié)點數(shù)據(jù)struct SListNode* next;//指針變量保存下一個節(jié)點的地址
}SLTNode;
尾插
//公共部分,開辟一個新的節(jié)點
SLTNode* SLBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}//鏈表的尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLBuyNode(x);//鏈表為空,新節(jié)點作為pheadif (*pphead == NULL){*pphead = newnode;return;}//鏈表不為空,找尾結點SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}//ptril就是尾結點,將尾結點指向newnodeptail->next = newnode;
}
頭插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLBuyNode(x);//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}
尾刪
void SLTPopBack(SLTNode** pphead)
{assert(pphead);//鏈表不能為空assert(*pphead);//指向第一個節(jié)點的地址//鏈表不為空//鏈表只有一個節(jié)點/多個節(jié)點if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;//銷毀尾結點free(ptail);ptail = NULL;
}
頭刪
void SLTPopFront(SLTNode** pphead)
{assert(pphead);//鏈表不能為空assert(*pphead);//讓第二個節(jié)點稱為新的頭SLTNode* next = (*pphead)->next;//->優(yōu)先級高于*//把舊的頭節(jié)點釋放掉free(*pphead);*pphead = next;
}
查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{assert(*pphead);//遍歷鏈表SLTNode* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}//沒有找到當前數(shù)據(jù)return NULL;
}
在指定位置之前插入數(shù)據(jù)
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//鏈表不能為空assert(*pphead);//pos剛好是頭結點if (pos == *pphead){//頭插SLTPushFront(pphead, x);return;}//pos不是頭結點的情況SLTNode* newnode = SLBuyNode(x);SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//找到了prev->next = newnode;newnode->next = pos;
}
在指定位置之后插入數(shù)據(jù)
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLBuyNode(x);newnode->next = pos->next;pos->next = newnode;
}
刪除指定位之前的節(jié)點
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(*pphead);assert(pos);//pos剛好是頭結點,沒有前驅節(jié)點,執(zhí)行頭刪if (*pphead == pos){//頭刪SLTPopFront(pphead);return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
刪除指定位置之后pos節(jié)點
//刪除指定位置之后pos節(jié)點
void SLTEraseAfter(SLTNode** pphead, SLTNode* pos)
{assert(pos);assert(pos->next);//pos pos->next pos->next->nextSLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}}
銷毀鏈表
//銷毀鏈表
void SListDesTory(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
3. 完整代碼
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
#include <stdio.h>//void SlistTest1()
//{
// //一般不會這樣去創(chuàng)建鏈表,這里只是為了給大家展示鏈表的打印
// SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
// node1->data = 1;
// SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
// node2->data = 2;
// SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
// node3->data = 3;
// SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
// node4->data = 4;
//
// node1->next = node2;
// node2->next = node3;
// node3->next = node4;
// node4->next = NULL;
//
// SLTNode* plist = node1;//定一個指針指向第一個節(jié)點
// SLTPrint(plist);
//}void SlistTest2()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 0);SLTPrint(plist);//SLTPushFront(&plist, 2);//SLTPushFront(&plist, 3);//SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);
}void SlistTest3()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 0);SLTPrint(plist);//頭刪SLTPopFront(&plist);SLTPrint(plist);//查找SLTNode* FindRet = SLTFind(&plist, 0);if (FindRet){printf("找到了\n");}else {printf("未找到\n");}SLTInsert(&plist, FindRet, 100);SLTPrint(plist);SLTInsertAfter(FindRet, 200);SLTPrint(plist);//刪除指定位置的節(jié)點SLTErase(&plist, FindRet);SLTPrint(plist);//銷毀節(jié)點SListDesTory(&plist);
}int main()
{SlistTest3();//SlistTest2();//SlistTest1();return 0;
}
SList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
#include <assert.h>//鏈表的打印
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}SLTNode* SLBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}//鏈表的尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLBuyNode(x);//鏈表為空,新節(jié)點作為pheadif (*pphead == NULL){*pphead = newnode;return;}//鏈表不為空,找尾結點SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}//ptril就是尾結點,將尾結點指向newnodeptail->next = newnode;
}//頭插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLBuyNode(x);//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}//尾刪
void SLTPopBack(SLTNode** pphead)
{assert(pphead);//鏈表不能為空assert(*pphead);//指向第一個節(jié)點的地址//鏈表不為空//鏈表只有一個節(jié)點/多個節(jié)點if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;//銷毀尾結點free(ptail);ptail = NULL;
}//頭刪
void SLTPopFront(SLTNode** pphead)
{assert(pphead);//鏈表不能為空assert(*pphead);//讓第二個節(jié)點稱為新的頭SLTNode* next = (*pphead)->next;//->優(yōu)先級高于*//把舊的頭節(jié)點釋放掉free(*pphead);*pphead = next;
}//查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{assert(*pphead);//遍歷鏈表SLTNode* pcur = *pphead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}//沒有找到當前數(shù)據(jù)return NULL;
}//在指定位置之前插入數(shù)據(jù)
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//鏈表不能為空assert(*pphead);//pos剛好是頭結點if (pos == *pphead){//頭插SLTPushFront(pphead, x);return;}//pos不是頭結點的情況SLTNode* newnode = SLBuyNode(x);SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//找到了prev->next = newnode;newnode->next = pos;
}//在指定位置之后插入數(shù)據(jù)
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLBuyNode(x);newnode->next = pos->next;pos->next = newnode;
}//刪除指定位之前的節(jié)點
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(*pphead);assert(pos);//pos剛好是頭結點,沒有前驅節(jié)點,執(zhí)行頭刪if (*pphead == pos){//頭刪SLTPopFront(pphead);return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}//刪除指定位置之后pos節(jié)點
void SLTEraseAfter(SLTNode** pphead, SLTNode* pos)
{assert(pos);assert(pos->next);//pos pos->next pos->next->nextSLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}//銷毀鏈表
void SListDesTory(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
SList.h
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>
#include <stdlib.h>typedef int SLTDataType;
//鏈表是由節(jié)點組成的
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;//鏈表的打印
void SLTPrint(SLTNode* phead);//鏈表的頭插和尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);//鏈表的頭刪和尾刪
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x);//在指定位置之前插入數(shù)據(jù)
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在指定位置之后插入數(shù)據(jù)
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//刪除指定位置之前pos節(jié)點
void SLTErase(SLTNode** pphead, SLTNode* pos);
//刪除指定位置之后pos節(jié)點
void SLTEraseAfter(SLTNode** pphead, SLTNode* pos);//銷毀鏈表
void SListDesTory(SLTNode** pphead);
4. 鏈表的分類
鏈表的結構非常多樣,一下情況組合組合起來就有8種鏈表結構:
鏈表說明:
在單鏈表中,“頭結點”的“頭”和“帶頭”鏈表是兩個概念
單鏈表中提到的“頭結點”指的是第一個有效的節(jié)點
“帶頭”鏈表里的“頭”指的是無效的節(jié)點
雖然鏈表的種類非常之多,但是使用比較多的只有兩種:單鏈表(不帶頭單向不循環(huán)鏈表)和雙向鏈表(帶頭雙向循環(huán)鏈表)