簡介
鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域,由於不必按順序存儲,鏈表在插入的時候可以達到O(1)的複雜度,比順序錶快得多,但是查找一個節點或者訪問特定編號的節點則需要O(n)的時間,而線性表和順序表相應的時間複雜度分別是O(logn)和O(1)。
分類
鏈表分爲帶頭、不帶頭,單向、雙向,循環、非循環,所以共有 8 種情況,但並不是 8 種情況都常用,我們挑出兩種極端的情況進行演示,即無頭單向非循環鏈表和帶頭雙向循環鏈表。
無頭單向非循環鏈表:結構簡單,一般不會單獨用來存數據。實際中更多是作爲其他數據結構的子結構,如哈希桶、圖的鄰接表等等。另外這種結構在筆試面試中出現很多。
帶頭雙向循環鏈表:結構最複雜,一般用在單獨存儲數據。 實際中使用的鏈表數據結構,都是帶頭雙向循環鏈表。另外這個結構雖然結構複雜,但是使用代碼實現以後會發現結構會帶來很多優勢,實現反而簡單了,後面我們代碼實現了就知道了。
具體實現
無頭單向非循環鏈表:
LinkList.h
#pragma once
#include<stdio.h>
#include<malloc.h>
#include<assert.h>
//無頭單向非循環鏈表增刪查改實現
typedef size_t LDataType;
//鏈表的單個節點
typedef struct ListNode
{
LDataType data;
struct ListNode* next;
}ListNode;
//鏈表
typedef struct List
{
ListNode* head;
}List;
void ListInit(List* plist);
void ListDestory(List* plist);
ListNode* BuyListNode(LDataType data);
void ListPrint(List* plist);
void ListPushFront(List* plist, LDataType data);
void ListPopFront(List* plist);
void ListPushBack(List* plist, LDataType data);
void ListPopBack(List* plist);
ListNode* ListFind(List* plist, LDataType data);
void ListInsertAfter(ListNode* pos, LDataType data); // 在pos的後面進行插入
void ListErase(List* plist, ListNode* pos);
void ListEraseAfter(ListNode* pos);
void ListRemove(List* plist, LDataType data);
LinkList.c
#include"LinkList.h"
void ListInit(List* plist)
{
assert(plist);
plist->head = NULL;
}
void ListDestory(List* plist)
{
assert(plist);
for (ListNode* cur = plist->head; cur != NULL; cur = cur->next)
{
ListNode* next = cur->next;
free(cur);
}
plist->head = NULL;
}
ListNode* BuyListNode(LDataType data)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
assert(node);
node->data = data;
node->next = NULL;
return node;
}
void ListPrint(List* plist)
{
assert(plist);
for (ListNode *cur = plist->head; cur != NULL; cur = cur->next)
{
printf("%d->", cur->data);
}
printf("NULL\n");
}
void ListPushFront(List* plist, LDataType data)
{
assert(plist);
ListNode* node = BuyListNode(data);
assert(node);
node->next = plist->head;
plist->head = node;
}
void ListPopFront(List* plist)
{
assert(plist != NULL);//保證鏈表存在
assert(plist->head);//保證鏈表不爲空
ListNode* tmp = plist->head;
plist->head = tmp->next;
free(tmp);
}
void ListPushBack(List* plist, LDataType data)
{
assert(plist);
if (plist->head == NULL) {
ListPushFront(plist, data);
return;
}
ListNode *node = BuyListNode(data);
assert(node);
ListNode* cur = plist->head;
for (; cur->next != NULL; cur = cur->next)
{ }
cur->next = node;
}
void ListPopBack(List* plist)
{
assert(plist);
assert(plist->head);
ListNode* cur = plist->head;
if (cur->next == NULL) {
ListPopFront(plist);
return;
}
ListNode* pos = plist->head;
for (; cur->next != NULL; cur = cur->next)
{
pos = cur;
}
free(cur);
pos->next = NULL;
}
//查找給定數據在鏈表中的位置
ListNode* ListFind(List* plist, LDataType data)
{
assert(plist);
assert(plist->head);
ListNode* cur = plist->head;
while (cur!=NULL)
{
if (cur->data == data)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 在pos的後面進行插入
void ListInsertAfter(ListNode* pos, LDataType data)
{
ListNode* node = BuyListNode(data);
node->next = pos->next;
pos->next = node;
}
//根據位置刪除元素
void ListErase(List* plist, ListNode* pos)
{
assert(plist);
if (plist->head == pos)
{
ListPopFront(plist);
}
else if (pos->next == NULL)
{
ListPopBack(plist);
}
else
{
ListNode* cur = plist->head;
for (; cur != NULL; cur = cur->next)
{
}
cur->next = pos->next;
free(pos);
}
}
//刪除給定位置後面的元素
void ListEraseAfter(ListNode* pos)
{
ListNode* next = pos->next;
pos->next = next->next;
free(next);
}
//根據數據刪除元素
void ListRemove(List* plist, LDataType data)
{
assert(plist);
ListNode* cur = plist->head;
ListNode* temp = NULL;
if (cur->data == data) //當該數據是第一個節點時,直接頭刪
{
ListPopFront(plist);
return;
}
else
{
while (cur) //頭節點不爲空
{
temp = cur;
cur = cur->next;
//這裏的if...else語句順序不能交換,需要先排除掉特殊情況,即要刪除的數據不存在
//如果交換,當要刪除的數據不存在時,會陷入死循環
if (NULL == cur)
{
printf("Data is not exist!\n");
return;
}
else if (cur->data == data)
{
temp->next = cur->next;
free(cur);
break;
}
}
}
}
test.c
#include"LinkList.h"
void test()
{
List plist;
ListInit(&plist);
ListPushBack(&plist, 1);
ListPushBack(&plist, 2);
ListPushBack(&plist, 3);
ListPushBack(&plist, 4);
ListPushBack(&plist, 5);
ListPushBack(&plist, 6);
ListPrint(&plist);
ListPopFront(&plist);
ListPrint(&plist);
//ListNode* pos = ListFind(&plist, 2);
//ListErase(&plist, pos);
//ListPrint(&plist);
ListRemove(&plist, 8);
ListNode* pos = ListFind(&plist, 3);
ListInsertAfter(pos, 9);
ListPrint(&plist);
ListDestory(&plist);
}