链表
-
概念
链表是逻辑上顺序存储的,但不是物理上连续存储的。为什么这么说。因为链表是一个一个结点组成的,结点是由数据和指针组成的。有了指针,存储起来就很灵活。
-
分类
1、无头结点单向不循环链表
//C语言
typedef struct SListNode //创建单向链表
{
int data;//结点数据
struct SListNode* next;//指向下一节点的指针
}SL;
typedef struct SList
{
SL* head;//这不是头节点,是指向第一个结点的指针
}HEAD;
2、有头节点双向循环链表
//C语言
typedef struct DListNode
{
int data;
struct DListNode* next;//指向下一个结点 指后继节点
struct DListNode* prev;//指向前一个结点 指前驱结点
}DL;
typedef struct DList
{
DL* head;//指向头节点的指针
}DLS;
实现
还是一样,摆上代码。这里只是主说单链表。单链表比双向链表要难。双向链表也会贴上。既参考,也希望阅读本文章的道友和前辈指出不足。另外单链表在OJ上考的多哦。【OJ—— online judge在线判题系统】
========================================分界线===================================================
先贴上头文件。头文件有我们链表的声明与定义。还有我们要实现的接口,就是函数。先提前声明。
#pragma once
typedef struct SListNode //创建单向链表
{
int data;//结点数据
struct SListNode* next;//指向下一节点的指针
}SL;
typedef struct SList
{
SL* head;
}HEAD;
//链表的初始化
void sl_init(HEAD* psl);
//链表的销毁
void sl_destroy(HEAD* psl);
//打印链表
void sl_print(HEAD* psl);
//获取数据
SL* gain_data(int data);
//链表的头插
void sl_add_front(HEAD* psl, int num);
//链表的尾插
void sl_add_end(HEAD* psl, int num);
//链表的头删
void sl_del_front(HEAD* psl);
//链表的尾删
void sl_del_end(HEAD* psl);
//链表的查询
SL* sl_find(HEAD* psl, int num);
//链表的随机插入
void sl_insert(SL* pos ,int num);
//链表的随机删除
void sl_esare(SL* pos);
//链表的删除第一个数字
void sl_firstnum(HEAD* psl, int num);
头文件要实现的功能是有点长。后面进行分块进行,就一个代码块一个代码块的来。只是拆分。大部分注释在代码段里,只是解释不清楚在外面进行详细解释。
//C语言
#include "SListNode.h"
#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
SL slistnode;
HEAD head;
//链表的初始化
void sl_init(HEAD* psl)
{
psl->head = NULL; //空链表
}
//链表的销毁
void sl_destroy(HEAD* psl)
{
SL* sl;
SL* cur = psl->head; //用 cur 指向第一个结点。
while (cur!=NULL)
{
sl = cur->next; //sl 指向第二个几点
free(cur); //释放前一个结点
cur = sl; //依次循环,知道最后一个结点的下一个
}
psl->head = NULL;
}
//打印链表
void sl_print(HEAD* psl) //这个和销毁是一样的
{
SL* cur = psl->head;
while (cur!=NULL)
{
printf("%d-->",cur->data);
cur = cur->next;
}
printf("NULL\n");
}
前面的是开胃菜,现在才开始进行重要的接口的实现。
//C语言
SL* gain_data(int data) //这是获取结点。就是添加的时候用的。后面会会见到
{
SL* node = (SL*)malloc(sizeof(SL)); //申请一个SL这么大的内存
assert(node);
node->data = data; //data 存数据
node->next = NULL; //指向下一个指针先不处理,指向空
return node; //返回
}
//链表的前插
void sl_add_front(HEAD* psl, int num)
{
SL* node = gain_data(num); //是不是见到了这个获取的结点
assert(node);
node->next = psl->head; //结点指向的是 head指向的,也就是之前的第一个结点
psl->head = node; //head 就指向 node 了, node 成为新的第一个结点
}
//链表的尾插
void sl_add_end(HEAD* psl, int num)
{
if (psl->head == NULL) //先判断是不是空表
{
sl_add_front(psl,num); //空表的话,进行头插。
return;
}
SL* last = psl->head;
while (last->next != NULL) //不是的话,我们从第一个结点开始,找到最后一个结点
{
last = last->next; //空表的话,就没有 last->next。
}
SL* node = gain_data(num); //然后,一道插入的工序进行就 OK了
assert(node);
last->next = node;
}
》》我进行图解来说明头插。帮助记忆和理解。现在对关键的语句进行图解。
//C语言
//链表的头删
void sl_del_front(HEAD* psl)
{
assert(psl != NULL);
assert(psl->head != NULL); //删除,就得保证有结点。
SL* oldhead = psl->head; //用一个结点保存head指向的第一个结点
psl->head = psl->head->next; //head指向第二个结点
free(oldhead); //释放开始存储的第一个结点
}
//链表的尾删
void sl_del_end(HEAD* psl)
{
assert(psl != NULL);
assert(psl->head != NULL);
if (psl->head->next == NULL) //尾删,不仅要有结点,还有有俩
{
sl_del_front(psl); //没有的话,就得头删
return;
}
SL* cur = psl->head; //如果大于两个
while (cur->next->next != NULL)
{
cur = cur->next; //找到倒数第二个结点
}
SL* oldend = cur->next; //保存倒数第一个结点
cur->next = cur->next->next; //倒数第二个指向空
free(oldend); //释放倒数第一个
}
继续图解。太草率的话,请评论哈。
》》尾删。主要的语句。看一下。
//C语言
//链表的查询
SL* sl_find(HEAD* psl, int num)
{
assert(psl);
SL* cur = psl->head;
while (cur!=NULL)
{
if (cur->data == num)//进行遍历,来匹配。
{
return cur;
}
cur = cur->next;
}
return NULL;//没找到,就返回空
}
//链表的随机插入
void sl_insert(SL* pos, int num)
{
SL* node = gain_data(num); //又见到了这个gain_data()
node->next = pos->next; //外面来图解。
pos->next = node;
}
//链表的随机删除
void sl_esare( SL* pos)
{
SL* cur = pos->next;
pos->next = pos->next->next;
free(cur);
}
大致情形如下哈
//C语言
//链表的删除第一个遇见数字
void sl_firstnum(HEAD* psl, int num)//解释一下题目。因为链表里面存的数字,可能存在重复。
{ //比如:1,5,7,9,5,2 5 是重复的
//这个和随机删除是不一样的。随机删除是某个位置的后一个结点
//这个是当前结点
//所以难点就是要找到当前结点的前一个结点
SL *prev = NULL; //所以定义一个结点,我们来标记前一个结点
SL *cur = psl->head;
while (cur != NULL && cur->data != num) {
prev = cur;
cur = cur->next;
}
if (cur == NULL) { //如果空链表就啥都不做
return;
}
if (prev == NULL) {
sl_del_front(psl);// 这个时候只有一个结点,且正好是我们存 num的结点
return; //因为触发这个条件的话就是跳出循环后了
}
prev->next = cur->next;//这个是正常的删除了。跳过要删除的结点
free(cur); //释放,就over了
}
这个结点我就不画图了,其实还是比较好理解的。链表的基础就到这儿结束了。后面就是贴上双向链表的代码。画个图来区分两个的区别。
<''附:其实本来可以早点发布博客,由于参加了一个C语言的测试,结果对我的打击很大。因为觉得自己不应该是这个成绩。但是出乎我的意料。那么以后的学习路上,我会更加严格的要求自己,自己的问题很大,不管是心态,还是学习方法。都应该继续提高。打击是应该经常有的,这样才能认清自己。犯的错不应该在犯第二次。这篇文章就检讨自己吧。雷霆雨露,俱是天恩。道友们,我们一起加油。乾坤未定,你我皆黑马!>
》》》》先贴图吧,有了图可以看代码好理解。
//C语言
#pragma once
typedef struct DListNode
{
int data;
struct DListNode* next;
struct DListNode* prev;
}DL;
typedef struct DList
{
DL* head;
}DLS;
//初始化
void dl_init(DLS* dls);
//销毁
void dl_destroy(DLS* dls);
//查找
DL* dl_find(DLS* dls, int data);
//打印
void dl_print(DLS* dls);
//插入
//随即插入
void dl_randadd(DL* pos, int data);
//前插
void dl_frontadd(DLS* dls, int data);
//尾插
void dl_endtadd(DLS* dls, int data);
//删除
//随机删除
void dl_randdel(DL* pos);
//头删
void dl_frontdel(DLS* dls);
//尾删
void dl_enddel(DLS* dls);
//C语言
#include "DListNode.h"
#include<stdio.h>
#include<stdlib.h>
//创建结点
DL* get_point(int data)
{
DL* node = (DL*)malloc(sizeof(DL));
node->data = data;
node->next = node->prev = NULL;
return node;
}
//初始化
void dl_init(DLS* dls)
{
DL* head = get_point(0);
head->next = head;
head->prev = head;
dls->head = head;
}
//销毁
//清空
void dl_clear(DLS* dls)
{
DL* temp;
DL* fellow;
for (temp=dls->head->next; temp != dls->head; temp = fellow)
{
fellow = temp->next;
free(temp);
}
dls->head->next =dls->head;
dls->head->prev= dls->head;
}
void dl_destroy(DLS* dls)
{
dl_clear(dls);
free(dls->head);
}
//查找
DL* dl_find(DLS* dls, int data)
{
DL* temp = dls->head->next;
for (temp; temp != dls->head; temp = temp->next)
{
if (temp->data == data)
{
return temp;
}
}
return NULL;
}
//打印
void dl_print(DLS* dls)
{
DL* temp = dls->head->next;
for (temp; temp != dls->head; temp = temp->next)
{
printf("%d <==> ", temp->data);
}
printf("head\n");
}
//插入
//随机结点之前插入
void dl_randadd(DL* pos, int data)
{
DL* node = get_point(data);
node->next = pos;
node->prev = pos->prev;
pos->prev->next = node;
pos->prev = node;
}
//前插
void dl_frontadd(DLS* dls, int data)
{
//DL_randadd(dls->head->next, data);
DL* node = get_point(data);
node->next = dls->head->next;
node->prev = dls->head;
dls->head->next->prev = node;
dls->head->next = node;
}
//尾插
void dl_endtadd(DLS* dls, int data)
{
//DL_randadd(dls->head, data);
DL* node = get_point(data);
node->next = dls->head;
node->prev = dls->head->prev;
dls->head->prev->next = node;
dls->head->prev = node;
}
//随机删除
void dl_randdel(DL* pos)
{
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
}
//头删
void dl_frontdel(DLS* dls)
{
DL* temp = dls->head->next;
dls->head->next->next->prev = dls->head;
dls->head->next = dls->head->next->next;
free(temp);
}
//尾删
void dl_enddel(DLS* dls)
{
DL* temp = dls->head->prev;
dls->head->prev->prev->next = dls->head;
dls->head->prev = dls->head->prev->prev;
free(temp);
}
尾:欢迎前辈和道友指出错误,评论啊。谢过>_<