C語言數(shù)據(jù)結(jié)構(gòu)之單向鏈表詳解
鏈表
鏈表實(shí)現(xiàn)了,內(nèi)存零碎數(shù)據(jù)的有效組織。比如,當(dāng)我們用 malloc 來進(jìn)行內(nèi)存申請(qǐng)的時(shí)候,當(dāng)內(nèi)存足夠,但是由于碎片太多,沒有連續(xù)內(nèi)存時(shí),只能以申請(qǐng)失敗而告終,而用鏈表這種數(shù)據(jù)結(jié)構(gòu)來組織數(shù)據(jù),就可以解決上類問題。

靜態(tài)鏈表

#include <stdio.h>
// 1.定義鏈表節(jié)點(diǎn)
typedef struct node{
int data;
struct node *next;
}Node;
int main(int argc, char *argv[]) {
// 2.創(chuàng)建鏈表節(jié)點(diǎn)
Node a;
Node b;
Node c;
// 3.初始化節(jié)點(diǎn)數(shù)據(jù)
a.data = 1;
b.data = 3;
c.data = 5;
// 4.鏈接節(jié)點(diǎn)
a.next = &b;
b.next = &c;
c.next = NULL;
// 5.創(chuàng)建鏈表頭
Node *head = &a;
// 6.使用鏈表
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;//指向下一個(gè)節(jié)點(diǎn)
}
return 0;
}動(dòng)態(tài)鏈表
靜態(tài)鏈表的意義不是很大,主要原因,數(shù)據(jù)存儲(chǔ)在棧上,棧的存儲(chǔ)空間有限,不能動(dòng)態(tài)分配。所以鏈表要實(shí)現(xiàn)存儲(chǔ)的自由,要?jiǎng)討B(tài)的申請(qǐng)堆里的空間。
有頭鏈表

無頭鏈表

單向鏈表有有頭節(jié)點(diǎn)和無頭節(jié)點(diǎn)兩種列表, 有頭節(jié)點(diǎn)在列表的刪除,反轉(zhuǎn),插入等操作會(huì)比無頭節(jié)點(diǎn)簡單,但是不好之處就是多占用些空間,而且在多個(gè)鏈表結(jié)合處理的時(shí)候有頭鏈表每次都需要過濾頭節(jié)點(diǎn),而無頭鏈表直接完美融合 ,而且很多高級(jí)語言都是無頭鏈表的便于日后的擴(kuò)展 ,下面都是依據(jù)無頭節(jié)點(diǎn)進(jìn)行演示
定義鏈表節(jié)點(diǎn)
// 1.定義鏈表節(jié)點(diǎn)
typedef struct node {
void *data;
struct node *next;
} Node;創(chuàng)建鏈表
/**
* 創(chuàng)建鏈表
*/
Node *createList() {
// 1.創(chuàng)建頭節(jié)點(diǎn)
Node *root = (Node *) malloc(sizeof(Node));
root->data = NULL;
root->next = NULL;
//返回頭節(jié)點(diǎn)
return root;
}
創(chuàng)建一個(gè)空節(jié)點(diǎn)
/**
* 創(chuàng)建一個(gè)空節(jié)點(diǎn)
*/
Node *createNode() {
Node *node = (Node *) malloc(sizeof(Node));
node->data = NULL;
node->next = NULL;
return node;
}尾插法

/**
* @brief insertEndNode 尾插法插入節(jié)點(diǎn)
* @param head 需要插入的頭指針
* @param data 需要插入的數(shù)據(jù)
*/
void insertEndNode(Node *node, void *data) {
Node *head = node;
//找到數(shù)據(jù)為空的節(jié)點(diǎn),沒有節(jié)點(diǎn)那么就擴(kuò)展
while (head->data != NULL) {
//擴(kuò)容
if(head->next == NULL) {
Node *pNode = createNode();
head->next = pNode;
head = pNode;
break;
}
head = head->next;
}
//插入數(shù)據(jù)
head->data = data;
}頭插法

/**
* @brief insertHeadNode 頭插法插入節(jié)點(diǎn)
* @param head 需要插入的頭指針
* @param data 需要插入的數(shù)據(jù)
*/
void insertHeadNode(Node **node, void *data) {
Node *pNode = createNode();
pNode->data = data;
pNode->next = *node;
*node = pNode;
}
指定位置插入一個(gè)結(jié)點(diǎn)

簡單來說就是先找到需要插入的位置,然后將當(dāng)前位置節(jié)點(diǎn)和他前一個(gè)節(jié)點(diǎn)連接到需要插入的節(jié)點(diǎn)就行了
/**
* @brief insertNOde 將數(shù)據(jù)節(jié)點(diǎn)插入到指定數(shù)據(jù)位置節(jié)點(diǎn),指定數(shù)據(jù)節(jié)點(diǎn)向后移動(dòng), 如果沒有找到插入的位置,那么就插入到最后
* @param insertionPoint 指定的數(shù)據(jù)節(jié)點(diǎn)
* @param data 需要插入的數(shù)據(jù)
*/
void insertNode(Node *node ,void *insertionPoint,void *data){
Node *pNode = node;
Node *pre = pNode;//父節(jié)點(diǎn)
while (pNode!=NULL){
if(pNode->data == insertionPoint){
break;
}
pre=pNode; //保留當(dāng)前節(jié)點(diǎn)
pNode=pNode->next;
}
//如果沒有找到那么就插入到最后
if(pNode==NULL){
insertEndNode(node,data);
return;
}
Node *dataNode = createNode();
dataNode->data= data;
pre->next = dataNode;
dataNode->next = pNode;
}遍歷鏈表
因?yàn)槲覀冎荡鎯?chǔ)是使用無類型的指針,所以需要轉(zhuǎn)換為插入時(shí)候的類型才行
/**
* @brief printNodeListDouble 遍歷鏈表
* @param node 鏈表指針頭
*/
void printNodeListDouble(Node *node) {
Node *head = node;
while (head!=NULL&&head->data!=NULL){
double *currentData = head->data;
printf("currentData = %f\n", *currentData);
head = head->next;
}
}
獲取鏈表長度
/**
* @brief listLength 計(jì)算鏈表長度
* @param head 鏈表頭指針
* @return 鏈表長度
*/
int listLength(Node *head){
int count = 0;
head = head;
while(head){
count++;
head = head->next;
}
return count;
}
鏈表搜索
/**
* @brief searchList 查找指定節(jié)點(diǎn)
* @param head 鏈表頭指針
* @param key 需要查找的值
* @return
*/
Node *searchList(Node *head, void *key){
head = head;
while(head){
if(head->data == key){
break;
}else{
head = head->next;
}
}
return head;
}鏈表數(shù)據(jù)排序
因?yàn)槲覀兇鎯?chǔ)數(shù)據(jù)類型是使用無類型的指針,那么在排序的時(shí)候需要轉(zhuǎn)換為指定類型才行
/**
* @brief bubbleSort 對(duì)鏈表值進(jìn)行排序 小到大
* @param head 鏈表頭指針
*/
void sortDouble(Node *head){
// 1.計(jì)算鏈表長度
int len = listLength(head);
// 2.定義變量記錄前后節(jié)點(diǎn)
Node *cur = head;
while (cur!=NULL){
Node *cur1=cur->next;
while (cur1!=NULL){
//比較大小 ,然后交換數(shù)據(jù)
if(*((double *)cur->data) > *((double *)cur1->data)){
double *temp = (double *)cur->data;
cur->data = cur1->data;
cur1->data = temp;
}
cur1 = cur1->next;
}
cur= cur->next;
}
}反轉(zhuǎn)鏈表
斷開鏈表頭,然后以頭插法的方式將原鏈表的數(shù)據(jù)添加鏈表。




/**
* @brief reverseList 反轉(zhuǎn)鏈表
* @param head 鏈表頭指針
*/
void reverseList(Node **root){
Node *head = *root;
Node *pre=head, *cur=head->next;
pre->next = NULL;
while (cur!=NULL){
Node *node = cur->next;
cur->next = pre;
pre = cur;
cur=node;
}
*root=pre;
}刪除節(jié)點(diǎn)數(shù)據(jù)

先找到需要?jiǎng)h除的節(jié)點(diǎn),之后將后一個(gè)結(jié)點(diǎn)賦值給前一個(gè)結(jié)點(diǎn)的next,然后將刪除位置的結(jié)點(diǎn)釋放即可
/**
* @brief delNode 刪除指定節(jié)點(diǎn)
*/
void delNode(Node **root, void *key){
Node *head = *root;
//根節(jié)點(diǎn)單獨(dú)處理
if(head->data == key&&head->next != NULL){
*root = head->next;
free(head->data);
free(head);
return;
}
Node *head1 = head->next;
Node *pre = head;//根節(jié)點(diǎn)
while (head1!=NULL){
if(head1->data == key){
pre->next = head1->next;
free(head1->data);
free(head1);
break;
}
pre = head1;//當(dāng)前節(jié)點(diǎn)
head1 = head1->next; //下一個(gè)節(jié)點(diǎn)
}
}銷毀鏈表
/**
* @brief destroyList 銷毀鏈表
* @param head 鏈表頭指針
*/
void destroyList(Node *head){
Node *cur = head;
while(head != NULL){
cur = head->next;
free(head->data);//清除當(dāng)前節(jié)點(diǎn)數(shù)據(jù)內(nèi)存
free(head);//清除當(dāng)前節(jié)點(diǎn)內(nèi)存
head = cur;
}
}
測試
int main(int argc, char *argv[]) {
//創(chuàng)建鏈表
Node *head = createList();
//插入數(shù)據(jù)
printf("insert ====================\n");
double *f = (double *)malloc(sizeof(double));
*f=2.1;
insertEndNode(head, f);
double *f1 = (double *)malloc(sizeof(double));
*f1=1.1;
insertEndNode(head, f1);
double *f2= (double *)malloc(sizeof(double));
*f2=0.1;
insertEndNode(head, f2);
double *f3= (double *)malloc(sizeof(double));
*f3=3.1;
insertHeadNode(&head, f3);
double *se = (double *)malloc(sizeof(double));
*se=111.1;
double *f4= (double *)malloc(sizeof(double));
*f4=7.1;
insertNode(head,se, f4);
free(se);
printNodeListDouble(head);
//獲取長度
printf("listLength ====================\n");
int i = listLength(head);
printf("listLength = %d\n", i);
//搜索
printf("searchList ====================\n");
Node *pNode = searchList(head, f1);
printf("currentData = %f\n", *((double *)pNode->data));
//排序
printf("sortDouble ====================\n");
sortDouble(head);
printNodeListDouble(head);
//反轉(zhuǎn)
printf("reverseList ====================\n");
reverseList(&head);
printNodeListDouble(head);
//刪除節(jié)點(diǎn)
printf("delNode ====================\n");
delNode(&head, f1);
printNodeListDouble(head);
//銷毀
destroyList(head);
return 0;
}

到此這篇關(guān)于C語言數(shù)據(jù)結(jié)構(gòu)之單向鏈表詳解的文章就介紹到這了,更多相關(guān)C語言單向鏈表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++虛函數(shù)表與類的內(nèi)存分布深入分析理解
對(duì)C++ 了解的人都應(yīng)該知道虛函數(shù)(Virtual Function)是通過一張?zhí)摵瘮?shù)表(Virtual Table)來實(shí)現(xiàn)的。簡稱為V-Table。本文就將詳細(xì)講講虛函數(shù)表的原理與使用,需要的可以參考一下2022-08-08

