数据结构—链表

链表

  • 概念

      链表是逻辑上顺序存储的,但不是物理上连续存储的。为什么这么说。因为链表是一个一个结点组成的,结点是由数据和指针组成的。有了指针,存储起来就很灵活。

  • 分类

      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);
}

尾:欢迎前辈和道友指出错误,评论啊。谢过>_<

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值