链表的使用
虽然有这么多的链表的结构,但是我们实际中最常⽤还是两种结构: 单链表 和 双向带头循环链表 1. ⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结 构的⼦结构,如哈希桶、图的邻接表等等。另外这种结构在笔试⾯试中出现很多。 2. 带头双向循环链表:结构最复杂,⼀般⽤在单独存储数据。实际中使⽤的链表数据结构,都 是带头双向循环链表。另外这个结构虽然结构复杂,但是使⽤代码实现以后会发现结构会带 来很多优势,实现反⽽简单了,后⾯我们代码实现了就知道了。
补充说明:
1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续
2、节点⼀般是从堆上申请的 3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续
SList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义链表节点的结构
typedef int SLDataType;
typedef struct SListNode{
int data;//要保存的数据
struct SListNode* next;
}SLNode;
//创建节点组成链表并打印链表
void SLPrint(SLNode* phead);
//尾插
void SLPushBack(SLNode** pphead, SLDataType x);
void SLPushFront(SLNode** pphead, SLDataType x);
//尾删
void SLPopBack(SLNode** pphead);
void SLPopFront(SLNode** pphead);
//在指定位置之前插入数据
void SLInit(SLNode** pphead, SLNode* pos, SLDataType x);
//在指定位置之后插入数据
void SLInit(SLNode* pos, SLDataType x);
//找节点(考虑第一个参数为一级指针还是二级指针)
//因为不改变头节点,所以可以传一级指针
//但由于代码一致性原则(保持接口一致性),应该传二级指针
void SLFind(SLNode** pphead, SLDataType x);
//删除pos结点
void SLErase(SLNode** pphead, SLNode* pos);
//删除pos之后的结点
void SLEraseAfter(SLNode** pphead, SLNode* pos);
//销毁链表
void SLDesTory(SLNode** pphead);
SList.c
#define _CRT_SECURE_NO_WARNINGS
#include "SList.h"
void SLPrint(SLNode* phead) {
//循环打印、
SLNode* pcur = phead;
while (pcur) {
printf("%d ->", pcur->data);
pcur = pcur->next;
}
printf("NULL\n");
}
SLNode* SLBuyNode(SLDataType x) {
SLNode* node = (SLNode*)malloc(sizeof(SLNode));
node->data = x;
node->next = NULL;
return node;
}
//尾插
void SLPushBack(SLNode** pphead, SLDataType x) {
assert(pphead);
SLNode* node = SLBuyNode(x);
if (*pphead = NULL) {
*pphead = node;
return;
}
//链表不为空,找尾(定义一个临时变量pcur)
SLNode* pcur= *pphead;
while (pcur->next) {
pcur = pcur->next;
}
pcur->next = node;
}
void SLPushFront(SLNode** pphead, SLDataType x) {
assert(pphead);
SLNode* node = SLBuyNode(x);
//新节点跟头结点连接起来
node->next = *pphead;//plist
//让新的节点成为头结点
*pphead = node;
}
void SLPopBack(SLNode** pphead) {
assert(pphead);
//第一个节点不能为空
assert(*pphead);
//只有一个节点的情况
if ((*pphead)->next==NULL) {
//直接删除头结点
free(*pphead);
pphead = NULL;
return;
}
//有多个结点的情况
//找到尾结点的前一个节点
SLNode* prev = NULL;
SLNode* ptail = *pphead;
while (ptail->next != NULL) {
prev = ptail;
ptail = ptail->next;
}
//prev的next指针不在指向ptail,而是指向ptail的下一个节点
prev->next = ptail->next;
free(ptail);
ptail = NULL;
}
void SLPopFront(SLNode** pphead) {
assert(pphead);
assert(*pphead);
SLNode* del = *pphead;
*pphead = (*pphead)->next;
free(del);
del = NULL;
}
void SLInit(SLNode** pphead, SLNode* pos, SLDataType x) {
assert(pphead);
SLNode* node = SLBuyNode(x);
//处理没有结点的情况(约定链表不能为空+pos也不能为空)
assert(pos);
assert(*pphead);
//处理只有一个结点+pos指向第一个结点(pos即为第一个结点)
if ((*pphead)->next == NULL||pos==*pphead) {
node->next = *pphead;
*pphead = node;
return;
}
//找pos的前一个节点
SLNode* prev = *pphead;
while (prev->next != NULL) {
prev = prev->next;
}
prev->next = pos;
pos->next = node;
}
//查找第一个为x的节点
void SLFind(SLNode** pphead, SLDataType x) {
SLNode* pcur = *pphead;
while (pcur) {
if (pcur->data == x) {
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
//删除pos结点
void SLErase(SLNode** pphead, SLNode* pos) {
assert(pphead);
assert(*pphead);
assert(pos);
if (pos == *pphead) {
*pphead = (*pphead)->next;
free(pos);
return;
}
//找pos的前一个节点
SLNode* prev = *pphead;
while (prev->next!=pos) {
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos=NULL;
}
//删除pos之后的结点
void SLEraseAfter(SLNode** pphead, SLNode* pos) {
assert(pos && pos->next);
SLNode* del = pos->next;
free(del);
del = NULL;
}
//销毁链表
void SLDesTory(SLNode** pphead) {
assert(pphead);
SLNode* pcur = *pphead;
//循环删除
while (pcur) {
SLNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
//int removeElement(int* nums, int numsSize, int val) {
// int src, dst;
// while (src < numsSize) {
// if (nums[src] == val) {
// src++;
// }
// else {
// nums[dst] = nums[src];
// src++;
// dst++;
// }
// }
// return dst;
//}
//void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
// int l1 = m - 1, l2 = n - 1;
// int l3 = m + n - 1;
// while (l1 >= 0 && l2 >= 0) {
// if (nums1[l1] > nums2[l2]) {
// nums1[l3--] = nums1[l1--];
// }
// else {
// nums1[l3--] = nums2[l2--];
// }
// }
// while (l2 >= 0) {
// nums1[l3--] = nums2[l2--];
// }
//}
#include"SList.h"
void slttest() {
SLNode* node1 = (SLNode*)malloc(sizeof(SLNode));
node1->data = 1;
SLNode* node2 = (SLNode*)malloc(sizeof(SLNode));
node2->data = 2;
SLNode* node3 = (SLNode*)malloc(sizeof(SLNode));
node3->data = 3;
SLNode* node4 = (SLNode*)malloc(sizeof(SLNode));
node4->data = 4;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
SLNode* plist = node1;
SLPrint(plist);
}
int main() {
slttest();
return 0;
}