文章目录
引言
线性表的另一种表示方法—— 链式存储结构,由于它不要求逻辑上相邻的数据元素在物理位置上也相邻,因此它没有顺序存储结构所具有的弱点,但同时也失去了顺序表可随机存取的优点。
带头单向链表
逻辑图示:
头文件设计singlelinklist.h
#ifndef SINGLELINK_H
#define SINGLELINK_H
//带头结点的单向链表结构设计
//设计结点类型
typedef int ElemType;
typedef struct ListNode {
ElemType val;
struct ListNode* next; //后继
}ListNode;
//链表
typedef struct LinkList {
ListNode* head; //指向头结点
int nodesize; //有效结点个数
}LinkList;
//1. 初始化
void InitList(LinkList* plist);
//2. 清空链表元素 将有效结点删除
void Clear(LinkList* plist);
//3. 销毁链表 清空链表+删除头结点
void Destory(LinkList* plist);
//4. 判空操作
bool IsEmpty(LinkList* plist);
//5. 头插数据
bool InsertHead(LinkList* plist, ElemType val);
//6. 尾插
bool InsertTail(LinkList* plist, ElemType val);
//7. 指定位置插入
bool InsertPos(LinkList* plist,int pos, ElemType val);
//8. 头删
bool DeleteHead(LinkList* plist);
//9. 尾删
bool DeleteTail(LinkList* plist);
//10. 按位置删除
bool DeletePos(LinkList* plist, int pos);
//10. 按位置删除 返回删除节点的元素值
ElemType DeletePos1(LinkList* plist, int pos);
//11. 删除val元素对应的结点
bool DeleteVal(LinkList* plist, ElemType val);
//12. 获取指定结点的前驱结点 12 23 34 45
ListNode*GetPrevNode(LinkList* plist, ElemType val);
//13. 获取指定结点的后驱结点
ListNode*GetNextNode(LinkList* plist, ElemType val);
//14. 删除指定结点的前驱结点
bool DeletePrevNode(LinkList* plist, ElemType val);
//15. 删除指定结点的后驱结点
bool DeleteNextNode(LinkList* plist, ElemType val);
//16. 打印链表
void Show(LinkList* plist);
#endif // ! SINGLELINK_H
#pragma once
接口函数实现singlelinklist.cpp
#include "singlelinklist.h"
#include <stdlib.h>
#include <string.h>
#include <cassert>
#include <stdio.h>
//申请一个节点
ListNode* Buynode(ElemType val,ListNode*next) {
ListNode* p = (ListNode*)malloc(sizeof(ListNode));
if (p == NULL) return nullptr;
//memset(p,0,sizeof(ListNode));
//可以初始化p指向的节点的val为空,p->next为空
p->val = val;
p->next = next;
return p;
}
//1. 初始化
void InitList(LinkList* plist) { //ListNode* head size
assert(plist != nullptr);
plist->head = Buynode(0,nullptr); //带头结点
plist->nodesize = 0;
}
//2. 清空链表元素 将有效结点删除
void Clear(LinkList* plist) {
assert(plist!= nullptr);
ListNode* p;
while (plist->head->next != nullptr) {
//对于空节点,有效节点为0,
//plist->head!=nullptr,后面 plist->head->next为0,p->next 就会出错,空不能解引用
p= plist->head->next;
plist->head->next = p->next;//p->next=nullptr也可以
free(p);
plist->nodesize--;
}
}
//如果是空链表的话,p就为空,p->next 就会出错,空不能解引用
//3. 销毁链表 清空链表+删除头结点
void Destory(LinkList* plist) {
assert(plist != nullptr);
Clear(plist);
free(plist->head);
plist->head = nullptr;//防止野指针
}
//4. 判空操作 TODO:
bool IsEmpty(LinkList* plist) {
assert(plist != nullptr);
return plist->head->next == nullptr;
//return plist->nodesize==0;
}
//5. 头插数据 TODO:
bool InsertHead(LinkList* plist, ElemType val) {
assert(plist != nullptr);
ListNode* s = Buynode(val, 0);
assert(s != nullptr);
s->next = plist->head->next;//如果有效节点为0,p->next=nullptr
plist->head->next = s;
plist->nodesize++;
return true;
}
//6. 尾插
bool InsertTail(LinkList* plist, ElemType val) {
assert(plist != nullptr);
ListNode* newnode = Buynode(val, nullptr);
assert(newnode != nullptr);
//找尾巴节点,该节点特点为.next=nullptr;
ListNode* p = plist->head;
while (p->next != nullptr) {
p = p->next;
}
p->next = newnode;
plist->nodesize++;
return true;
}
//7. 指定位置插入 pos第个有效的节点的后一个位置;
bool InsertPos(LinkList* plist, int pos,ElemType val) {
assert(plist != nullptr && pos >= 1 && pos <= plist->nodesize+1);
ListNode* newnode = Buynode(val, nullptr);
assert(newnode != nullptr);
ListNode* p = plist->head;
for (int i = 0; i < pos-1; i++) {
p = p->next;
}
newnode->next = p->next;
p->next = newnode;
plist->nodesize++;
return true;
}
//8. 头删
bool DeleteHead(LinkList* plist) {
assert(plist!=nullptr);
if (IsEmpty(plist))return false;
//指针指向待删除节点的地址
ListNode* p = plist->head->next;
//贯穿
plist->head = p->next;//如果一个节点,p->next=nullptr;
//释放p节点
free(p);
plist->nodesize--;
return true;
}
//9. 尾删
bool DeleteTail(LinkList* plist) {
assert(plist != nullptr);
if (IsEmpty(plist))return false;
ListNode* p = plist->head;
while (p->next->next != nullptr) {
p = p->next;
}
p->next = nullptr;
free(p->next->next);
plist->nodesize--;
return true;
}
//10. 按位置删除
bool DeletePos(LinkList* plist, int pos) {
assert(plist != nullptr&&pos>=1&&pos<=plist->nodesize);
ListNode* p = plist->head;
for (int i = 0; i < pos-1; i++) {
p = p->next;
}
p->next = p->next->next;
free(p->next);
plist->nodesize--;
return true;
}
//10. 按位置删除 返回删除节点的元素值
ElemType DeletePos1(LinkList* plist, int pos) {
assert(plist != nullptr);
if (IsEmpty(plist)) exit(EXIT_FAILURE);
ListNode* p = plist->head;
for (int i = 0; i < pos-1; i++) {
p = p->next;
}
ListNode* q = p->next;
ElemType val = q->val;
p->next = p->next->next;
free(q);
return val;
}
11. 删除val元素对应的结点 我自己的方法
//bool DeleteVal(LinkList* plist, ElemType val) {
// assert(plist != nullptr && plist->nodesize > 0);
// //1 2
// //
// //2 3 5 5 6 5 7 8 9 6 3
// int pos = 1;
// for (ListNode* p = plist->head->next; p != nullptr; p = p->next,pos++) {
// if (p->val == val) {
// DeletePos(plist, pos);
// pos--;
// }
// }
// return true;
//}
//11. 删除val元素对应的结点 1 2 3
bool DeleteVal(LinkList* plist, ElemType val) {
//assert
//找到该val所在结点的前驱结点,贯穿
if (IsEmpty(plist)) return false;
ListNode* q, * p = plist->head;
while (p->next != nullptr) {
if (p->next->val == val) {
q = p->next; //q标记的是待删结点 删除当前q
p->next = p->next->next;
free(q);
}
else {
p = p->next;
}
}
return true;
}
12. 获取指定结点的前驱结点 12 23 34 45
//ListNode GetPrevNode(LinkList* plist, ElemType val) {
// assert(plist != nullptr);
// ListNode* p = plist->head;
// while (p->next->val != val) {//没有考虑空链表的情况
// p = p->next;
// }
// return *p;
//}
//
//12. 获取指定结点的前驱结点 12 23 34 45
ListNode*GetPrevNode(LinkList* plist, ElemType val) {
//assert
if (IsEmpty(plist)) return nullptr;
ListNode* q = nullptr;
for (ListNode* p = plist->head; p->next != nullptr; p = p->next) {
if (p->next->val == val) {
q = p;
break;
}
}
return q;
}
//13. 获取指定结点的后驱结点
//ListNode *GetNextNode(LinkList* plist, ElemType val) {
// assert(plist != nullptr);
// ListNode* p = plist->head;
// while (p->next->val != val) {
// p = p->next;//没有该点,就会崩掉
// }
// if (p->next->next == nullptr)return;//防止崩掉
// return (p->next->next);
//}
ListNode* GetNextNode(LinkList* plist, ElemType val) {
//assert
ListNode* p = plist->head->next;
for (; p != nullptr && p->val != val; p = p->next) {
;
}
return p != nullptr ? p->next : nullptr;
}
//14. 删除指定结点的前驱结点
bool DeletePrevNode(LinkList* plist, ElemType val) {
//assert(plist != nullptr);
//ListNode* p = plist->head;
//int pos = 1;
//while (p->next->val != val) {//可能会崩
// p = p->next;
// pos++;
//}
//if (pos <= 1)return false;
//DeletePos(plist, pos-1);
if (IsEmpty(plist)) return false;
ListNode* q;
for (ListNode* p = plist->head; p->next->next != nullptr; p = p->next) {
if (p->next->next->val == val) { //p标记的是val所在结点的前前个
q = p->next;
p->next = p->next->next;// 删除val的前驱结点
free(q);
break;
}
}
return true;
}
//15. 删除指定结点的后驱结点
bool DeleteNextNode(LinkList* plist, ElemType val) {
assert(plist != nullptr);
if (IsEmpty(plist))return false;
ListNode* q;
for (ListNode* p = plist->head; p->next->next != nullptr;p=p->next ) {
if (p->next->val == val) {
q = p->next->next;
p->next->next = q->next;
free(q);
break;
}
}
return true;
}
//16. 打印链表
void Show(LinkList* plist) {
for (ListNode* p = plist->head->next; p != nullptr; p = p->next) {
printf("%5d", p->val);
}
printf("\n");
}
部分函数实现图示
初始化一个带头单链表
在ptr指针之后插入数据元素
删除ptr所指向的后续结点(数据元素)
不带头单向链表
逻辑图示:
头文件设计noheadsinglelinklist.h
#ifndef SINGLELINKNOHEAD_H
#define SINGLELINKNOHEAD_H
//不带头结点的单向链表结构设计
//设计结点类型
typedef int ElemType;
typedef struct ListNode {
ElemType val;
struct ListNode* next; //后继
}ListNode;
//链表
typedef struct LinkList {
ListNode* head; //指向头
int nodesize; //有效结点个数
}LinkList;
//1. 初始化
void InitList(LinkList* plist);
//2. 清空链表元素 将有效结点删除
void Clear(LinkList* plist);
//3. 销毁链表 清空链表+删除头结点
void Destory(LinkList* plist);
//4. 判空操作
bool IsEmpty(LinkList* plist);
//5. 头插数据
bool InsertHead(LinkList* plist, ElemType val);
//6. 尾插
bool InsertTail(LinkList* plist, ElemType val);
//7. 指定位置插入
bool InsertPos(LinkList* plist, int pos, ElemType val);
//8. 头删
bool DeleteHead(LinkList* plist);
//9. 尾删
bool DeleteTail(LinkList* plist);
//10. 按位置删除
ElemType DeletePos(LinkList* plist, int pos);
//11. 删除val元素对应的结点
bool DeleteVal(LinkList* plist, ElemType val);
//12. 获取指定结点的前驱结点 12 23 34 45
ListNode* GetPrevNode(LinkList* plist, ElemType val);
//13. 获取指定结点的后驱结点
ListNode* GetNextNode(LinkList* plist, ElemType val);
//14. 删除指定结点的前驱结点
bool DeletePrevNode(LinkList* plist, ElemType val);
//15. 删除指定结点的后驱结点
bool DeleteNextNode(LinkList* plist, ElemType val);
//16. 打印链表
void Show(LinkList* plist);
#endif // ! SINGLELINKNOHEAD_H
接口函数实现noheadsinglelinklist.cpp
#include"noheadsinglelinklist.h"
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
//不带头节点链表实现
ListNode* BuyNode(ElemType val, ListNode* next) {
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
if (node == nullptr)return nullptr;
node->next = next;
node->val = val;
return node;
}
//1. 初始化
void InitList(LinkList* plist) {
assert(plist != nullptr);
// 带头结点: plist->head = BuyNode(0, nullptr);
//不带头
plist->head = nullptr;
plist->nodesize = 0; //当前有效结点个数0
}
//2. 清空链表元素 将有效结点删除
void Clear(LinkList* plist) {
assert(plist != nullptr);
ListNode* p; //不带头结点,head 是指针 指向有效结点
while (plist->head != nullptr) {
DeleteHead(plist);
}
}
//3. 销毁链表 清空链表+不存在头结点
void Destory(LinkList* plist) {
assert(plist != nullptr);
Clear(plist);
}
//4. 判空操作
bool IsEmpty(LinkList* plist) {
assert(plist != nullptr);
//带头结点写法:return plist->head->next == nullptr;
//不带头结点写法
return plist->head == nullptr;
}
//5. 头插数据
bool InsertHead(LinkList* plist, ElemType val) {
assert(plist != nullptr);
ListNode* p = BuyNode(val, 0);
assert(p != nullptr);
p->next = plist->head;
plist->head = p;
plist->nodesize++;
return true;
}
//6. 尾插
bool InsertTail(LinkList* plist, ElemType val) {
assert(plist != nullptr);
ListNode* newnode = BuyNode(val, nullptr);
assert(newnode != nullptr);
if (IsEmpty(plist)) {
plist->head = newnode;
}
else {
ListNode* p = plist->head;
while (p->next != nullptr) {
p = p->next;
}
p->next = newnode;
}
plist->nodesize++;
return true;
}
//7. 指定位置插入
bool InsertPos(LinkList* plist, int pos, ElemType val) {
assert(plist != nullptr && pos >= 1 && pos <= plist->nodesize);
ListNode* newnode = BuyNode(val, nullptr);
assert(newnode != nullptr);
if (pos == 1) {
InsertHead(plist, val); //跟带头结点区别
}
else {
ListNode* p = plist->head; //head ->1 2 3 4 pos 1
for (int i = 1; i < pos - 1; i++) { //找该位置前一个结点
p = p->next;
}
newnode->next = p->next;//先牵右
p->next = newnode;//再牵左
plist->nodesize++;
}
return true;
}
//8. 头删
bool DeleteHead(LinkList* plist) {
assert(plist != nullptr);
if (IsEmpty(plist))return false;
ListNode* p = plist->head;
plist->head = p->next;
free(p);
plist->nodesize--;
return true;
}
//9. 尾删
bool DeleteTail(LinkList* plist) {
assert(plist != nullptr);
if (IsEmpty(plist))return false;
if (plist->nodesize == 1) {
free(plist->head);
plist->head = nullptr;
}
else {
ListNode* p = plist->head;
while (p->next->next != nullptr) {
p = p->next;
}
free(p->next);
p->next = nullptr;
}
plist->nodesize--;
return true;
}
///以下代码针对不带头结点如何修改
//10. 按位置删除 返回删除结点的元素值
ElemType DeletePos(LinkList* plist, int pos) {
//assert
if (IsEmpty(plist)) exit(EXIT_FAILURE);
ListNode* q, * p = plist->head;
ElemType val;
if (pos == 1) {
q = p;
val = q->val;
p = p->next;
free(q);
}
else {
for (int i = 1; i < pos - 1; i++) { // p标记的是待删节点前一个
p = p->next;
}
q = p->next; //q标记的是待删结点
val = q->val;
//删除当前q
p->next = p->next->next;
free(q);
}
return val;
}
//11. 删除val元素对应的结点 1 2 2 3
bool DeleteVal(LinkList* plist, ElemType val) {
//assert
//找到该val所在结点的前驱结点,贯穿
if (IsEmpty(plist)) return false;
ListNode* q ;
ListNode*p = plist->head;
while (p->val==val)
{
q = p; //q标记的是待删结点 删除当前q
plist->head = p->next;
free(q);
p = plist->head;
}
while (p->next != nullptr) {
if (p->next->val == val) {//使用未初始化的内存*p
q = p->next; //q标记的是待删结点 删除当前q
p->next = p->next->next;
free(q);
}
else {
p = p->next;
}
}
return true;
}
12. 获取指定结点的前驱结点 12 23 34 45
//ListNode GetPrevNode(LinkList* plist, ElemType val) {
// assert(plist != nullptr);
// ListNode* p = plist->head;
// while (p->next->val != val) {//没有考虑空链表的情况
// p = p->next;
// }
// return *p;
//}
//
//12. 获取指定结点的前驱结点 12 23 34 45
ListNode* GetPrevNode(LinkList* plist, ElemType val) {
//assert
if (IsEmpty(plist)) return nullptr;
ListNode* q = nullptr;
for (ListNode* p = plist->head; p->next != nullptr; p = p->next) {
if (p->next->val == val) {
q = p;
break;
}
}
return q;
}
//13. 获取指定结点的后驱结点
//ListNode *GetNextNode(LinkList* plist, ElemType val) {
// assert(plist != nullptr);
// ListNode* p = plist->head;
// while (p->next->val != val) {
// p = p->next;//没有该点,就会崩掉
// }
// if (p->next->next == nullptr)return;//防止崩掉
// return (p->next->next);
//}
ListNode* GetNextNode(LinkList* plist, ElemType val) {
//assert
ListNode* p = plist->head;
for (; p != nullptr && p->val != val; p = p->next) {
;
}
return p != nullptr ? p->next : nullptr;
}
//14. 删除指定结点的前驱结点
bool DeletePrevNode(LinkList* plist, ElemType val) {
//assert(plist != nullptr);
//ListNode* p = plist->head;
//int pos = 1;
//while (p->next->val != val) {//可能会崩
// p = p->next;
// pos++;
//}
//if (pos <= 1)return false;
//DeletePos(plist, pos-1);
if (IsEmpty(plist)) return false;
ListNode* q;
ListNode* p = plist->head;
if (p->next == nullptr)return false;
if (p->next->val == val && p->next != nullptr) {
DeleteHead(plist);
return true;
}
for (; p->next->next != nullptr; p = p->next) {
if (p->next->next->val == val) { //p标记的是val所在结点的前前个
q = p->next;
p->next = p->next->next;// 删除val的前驱结点
free(q);
break;
}
}
return true;
}
//15. 删除指定结点的后驱结点
bool DeleteNextNode(LinkList* plist, ElemType val) {
assert(plist != nullptr);
if (IsEmpty(plist))return false;
ListNode* q;
for (ListNode* p = plist->head; p->next != nullptr; p = p->next) {
if (p->val == val) {
q = p->next;
p->next = q->next;
free(q);
break;
}
}
return true;
}
//16. 打印链表
void Show(LinkList* plist) {
if (IsEmpty(plist))return;
for (ListNode* p = plist->head; p != nullptr; p = p->next) {
printf("%5d", p->val);
}
printf("\n");
}
带头双向循环链表
示意图
带头双向循环链表
插入一个新的节点
删除一个节点
头文件设计 结构体 设计
#include"DuLinkList.h"
#include<stdio.h>
#include<stdlib.h>
#include <cassert>
//带头双向循环链表
typedef int ElemType;
//有三个域,分别是数据域data 前面一个节点的地址,
//和后面一个节点的地址
typedef struct DuNode
{
struct DuNode* prev;
struct DuNode* next;
ElemType data;
}DuNode, * PDuNode;
//定义一个结构体DuLinkList 里面一个指针,指向一个链表,
//一个cuisize标记当前的节点个数
typedef struct DuLinkList
{
PDuNode head; //
int cursize; //
}DuLinkList;
函数接口实现
//购买一个节点,这个并且将这个点的next prev指向它自己
// 不带参数,就是用默认参数,就是空指针,
//该头结点的前驱和后继全都指向自身
DuNode* Buynode(DuNode* parg , DuNode* narg )//重定义默认参数1 2
{
DuNode* s = (DuNode*)malloc(sizeof(DuNode));
if (nullptr == s) exit(1); // .c NULL
s->prev = parg == nullptr ? s : parg;
s->next = narg == nullptr ? s : narg;
return s;
}
void Freenode(DuNode* p) {
free(p);
}
// 不带参数,就是用默认参数,该头结点的前驱和后继全都指向自身
//初始化DuLinkList 指向头结点
void InitList(DuLinkList* plist)
{
assert(plist != nullptr);
plist->cursize = 0;
plist->head = Buynode();
}
//由于是循环链表,所以头结点的前一个节点就是尾巴
//当p不为头结点,就可以一直释放p节点,当尾巴节点为头节点时,就可以
//结束,该链表为空,只留下了头结点
void Clear(DuLinkList* plist) {
assert(plist != nullptr);
DuNode* p = plist->head->prev;
while (p != plist->head) {
Freenode(p);
p = p->prev;
}
plist->cursize = 0;
}
//因为有头节点,所以销毁该链表,需要分两步,
// //第一步清空所有的数据节点,
//第二步,就是将头结点释放
void Destory(DuLinkList* plist) {
assert(plist != nullptr);
Clear(plist);
Freenode(plist->head);
plist->head = nullptr;
}
//在ptr指针所指向的节点的前面插入一个点
//注意顺序,ptr->prev= Buynode(ptr->prev, ptr);//连接了三根线
//ptr->prev 指向了一个新的节点,该新节点prev指向了ptr的前面的节点,next指向了ptr
//最后将s的prev的next指向s就连接完毕,最后记得cursize++
void Insert(DuLinkList* plist, DuNode* ptr, ElemType val)
{
assert(plist != nullptr && ptr != nullptr);
ptr->prev= Buynode(ptr->prev, ptr);//连接了三根线
DuNode* s = ptr->prev;
s->prev->next = s;
s->data = val;
plist->cursize++;
}
//利用Insert()进行头插和尾插
void Push_Front(DuLinkList* plist, ElemType val)
{
assert(plist != nullptr);
Insert(plist, plist->head->next, val);
}
void Push_Back(DuLinkList* plist, ElemType val)
{
assert(plist != nullptr);
Insert(plist, plist->head, val);
}
//删除节点贯穿的时候不用标记
void Earse(DuLinkList* plist, DuNode* ptr) {
assert(plist != nullptr&&ptr!=nullptr);
ptr->prev->next = ptr->next;
ptr->next->prev = ptr->prev;
Freenode(ptr);
plist->cursize--;
}
void PopFront(DuLinkList* plist) {
assert(plist != nullptr);
Earse(plist, plist->head->next);
}
void PopBack(DuLinkList* plist) {
assert(plist != nullptr);
Earse(plist, plist->head->prev);
}
int GetSize(DuLinkList* plist) {
assert(plist != nullptr);
return plist->cursize;
}
//用p指针遍历,当p->val==val 返回p的值,不等于val,往后遍历,如果么有
//该值,遍历一遍退出,即,p==plist->head;
DuNode* FindValue(DuLinkList* plist, ElemType val) {
assert(plist != nullptr);
DuNode* p = plist->head->next;
while (p!=plist->head&&p->data!=val)
{
p = p->next;
}
return p == plist->head ? nullptr : p;
}
bool IsEmpty(DuLinkList* plist) {
assert(plist != nullptr);
return plist->cursize == 0;
}
void Insertorder(DuLinkList*plist,ElemType val) {
assert(plist != nullptr);
DuNode* p = plist->head->next;
while (val>p->data)
{
p = p->next;
}
Insert(plist, p, val);
}
void Print(DuLinkList* plist) {
assert(plist != nullptr);
DuNode* p = plist->head->next;
while (p != plist->head) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}