链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的
注意:
- 链式结构在逻辑上是连续的,但是在物理上不一定连续
- 在链表实现过程中,结点一般都是从堆一个一个申请出来的
- 从堆上申请的空间,是按照系统来分配的,两次申请的空间可能连续的,也可能不是连续的
链表的分类
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
-
单向或者双向
-
带头或者不带头
-
循环或者不循环
在实际使用中,我们最常用这两种结构:
无头单向非循环链表:
带头双向循环链表:
今天我们学习单链表的实现
单链表实现思路:
尾插:
void SLTPushBack(SLNode** pphead, SLNDataType x)
{SLNode* newnode = CreateNode(x);if (*pphead == NULL){*pphead = newnode;}else{SLNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}SLNode* newnode = CreateNode(x);tail->next = newnode;}
}
头插:
void SLTPushFront(SLNode** pphead, SLNDataType x)
{SLNode* newnode = CreateNode(x);newnode->next = *pphead;*pphead = newnode;
}
尾删:
void SLTPopBack(SLNode** pphead)
{assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//方法1:/*SLNode* tail = *pphead;while (tail->next->next != NULL){tail = tail->next;}free(tail->next);tail->next = NULL;*///方法2:SLNode* tail = *pphead;SLNode* prev = NULL;while (tail != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}
头删:
void SLTPopFront(SLNode** pphead)
{assert(*pphead);//方法1:/*SLNode* tmp = *pphead;*pphead = tmp->next;free(tmp);tmp = NULL;*///方法2:/*SLNode* tmp = *pphead;*pphead = (*pphead)->next;free(tmp);*///错误://SLNode* tmp = *pphead;//free(tmp);//*pphead = (*pphead)->next;//这里先free(tmp)会导致*pphead后面存放的数据无法拿取,造成内存泄露//方法3:SLNode* tmp = (*pphead)->next;free(*pphead);*pphead = tmp;
}
在pos的前面插入:
SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{assert(pphead);assert(*pphead);assert(pos);if (pos == *pphead){//头插SLTPushFront(pphead, x);}else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLNode* newnode = CreateNode(x);newnode->next = pos;prev->next = newnode;}
}
删除pos位置:
void SLTErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(*pphead);//不允许链表为空assert(pos);//pos必需为有效节点if (*pphead == pos){SLTPopFront(pphead);}else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
单链表实现代码:
SList.h:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLNDataType;typedef struct SListNode
{int val;struct SListNode* next;
}SLNode;//打印
void SLTPrint(SLNode* phead);//尾插
void SLTPushBack(SLNode** pphead, SLNDataType x);
//头插
void SLTPushFront(SLNode** pphead, SLNDataType x);//尾删
void SLTPopBack(SLNode** pphead);
//头删
void SLTPopFront(SLNode** pphead);SLNode* SLTFind(SLNode* phead, SLNDataType x);// 在pos的前面插入
SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);// 删除pos位置
void SLTErase(SLNode** pphead, SLNode* pos);// 后面插入 后面删除
void SLTInsertAfter(SLNode* pos, SLNDataType x);
void SLTEraseAfter(SLNode* pos);// 前面插入
void SLTInsertHead(SLNode* pos, SLNDataType x);//销毁
void SLTDestroy(SLNode** pphead);
SList.c:
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"void SLTPrint(SLNode* phead)
{SLNode* cur = phead;while (cur != NULL){printf("%d ->", cur->val);cur = cur->next;}printf("NULL");
}SLNode* CreateNode(SLNDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("malloc fail");exit(-1);}newnode->val = x;newnode->next = NULL;return newnode;
}void SLTPushBack(SLNode** pphead, SLNDataType x)
{SLNode* newnode = CreateNode(x);if (*pphead == NULL){*pphead = newnode;}else{SLNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}SLNode* newnode = CreateNode(x);tail->next = newnode;}
}void SLTPushFront(SLNode** pphead, SLNDataType x)
{SLNode* newnode = CreateNode(x);newnode->next = *pphead;*pphead = newnode;
}void SLTPopBack(SLNode** pphead)
{assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//方法1:/*SLNode* tail = *pphead;while (tail->next->next != NULL){tail = tail->next;}free(tail->next);tail->next = NULL;*///方法2:SLNode* tail = *pphead;SLNode* prev = NULL;while (tail != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}void SLTPopFront(SLNode** pphead)
{assert(*pphead);//方法1:/*SLNode* tmp = *pphead;*pphead = tmp->next;free(tmp);tmp = NULL;*///方法2:/*SLNode* tmp = *pphead;*pphead = (*pphead)->next;free(tmp);*///错误://SLNode* tmp = *pphead;//free(tmp);//*pphead = (*pphead)->next;//这里先free(tmp)会导致*pphead后面存放的数据无法拿取,造成内存泄露//方法3:SLNode* tmp = (*pphead)->next;free(*pphead);*pphead = tmp;
}SLNode* SLTFind(SLNode* phead, SLNDataType x)
{SLNode* cur = phead;while (cur){if (cur->val == x){return cur;}else{cur = cur->next;}}return NULL;
}SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{assert(pphead);assert(*pphead);assert(pos);if (pos == *pphead){//头插SLTPushFront(pphead, x);}else{SLNode* prev = *pphead;while (prev){if (prev->next != pos){prev = prev->next;}else{SLNode* newnode = CreateNode(x);newnode->next = pos;prev->next = newnode;}}while (prev->next != pos){prev = prev->next;}SLNode* newnode = CreateNode(x);newnode->next = pos;prev->next = newnode;}
}void SLTErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(*pphead);//不允许链表为空assert(pos);//pos必需为有效节点if (*pphead == pos){SLTPopFront(pphead);}else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}void SLTInsertAfter(SLNode* pos, SLNDataType x)
{assert(pos);SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));newnode->next = pos->next;pos->next = newnode;newnode->val = x;
}void SLTEraseAfter(SLNode* pos)
{assert(pos);assert(pos->next);SLNode* tmp = pos->next;pos->next = pos->next->next;free(tmp);tmp = NULL;
}void SLTInsertHead(SLNode* pos, SLNDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));newnode->val = pos->val;newnode->next = pos->next;pos->val = x;pos->next = newnode;
}void SLTDestroy(SLNode** pphead)
{assert(pphead);SLNode* cur = *pphead;while (cur){SLNode* tmp = cur->next ;free(cur);cur = tmp;} *pphead = NULL;
}
Test.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"void TestSLT1()
{SLNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPrint(plist);printf("\n");SLTPushFront(&plist, 4);SLTPushFront(&plist, 5);SLTPrint(plist);printf("\n");}void TestSLT2()
{SLNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushBack(&plist, 5);SLTPrint(plist);printf("\n");SLTPopBack(&plist);SLTPopBack(&plist);SLTPopBack(&plist);SLTPrint(plist);
}void TestSLT3()
{SLNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushBack(&plist, 5);SLTPrint(plist);printf("\n");SLTPopFront(&plist);SLTPopFront(&plist);SLTPrint(plist);printf("\n");SLNode* pos = SLTFind(plist, 4);SLTInsert(&plist, pos, 30);SLTInsert(&plist, pos, 40);SLTPrint(plist);printf("\n");}void TestSLT4()
{SLNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);printf("\n");SLNode* pos = SLTFind(plist, 2);SLTErase(&plist, pos);SLTPrint(plist);printf("\n");pos = SLTFind(plist, 3);SLTInsertAfter(pos, 5);SLTPrint(plist);printf("\n");pos = SLTFind(plist, 1);SLTEraseAfter(pos);SLTPrint(plist);printf("\n");SLTDestroy(&plist);
}void TestSLT5()
{SLNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);printf("\n");SLNode* pos = SLTFind(plist, 2);SLTInsertHead(pos, 20);SLTPrint(plist);printf("\n");SLTDestroy(&plist);
}int main()
{TestSLT5();return 0;
}