IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 顺序表和链表 -> 正文阅读

[数据结构与算法]顺序表和链表

一.顺序表

1.概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表可分为:

1. 静态顺序表:使用定长数组存储元素
2. 动态顺序表:使用动态开辟的数组存储。

2.动态顺序表的实现

SeqList.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

typedef int SLDateType;

//动态顺序表
typedef struct SeqList
{
	SLDateType* a;
	int size;    // 存储数据个数
	int capacity;// 存储空间大小
}SL,SeqList;

//打印出数据
void SeqListPrint(SeqList* psl);

//初始化顺序表
void SeqListInit(SeqList* psl);

//销毁顺序表
void SeqListDestroy(SeqList* psl);

//检查容量
void SeqListCheckcapacity(SeqList* psl);

//在pos位置插入数据x
void SeqListInsert(SeqList* psl, size_t pos, SLDateType x);

//删除pos位置的数据
void SeqListErase(SeqList* psl, size_t pos);

//头插和头删  时间复杂度为 O(N)
void SeqListPopFront(SeqList* psl);//头删数据
void SeqListPushFront(SeqList* psl, SLDateType x);//头插数据

//尾插和尾删  时间复杂度为 O(1)
void SeqListPopBack(SeqList* psl);//尾删数据
void SeqListPushBack(SeqList* psl, SLDateType x);//尾插数据

//顺序表查找
int SeqListFind(SeqList* psl,SLDateType x);

SeqList.c

#include "SeqList.h"
void SeqListPrint(SeqList* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; ++i)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}

void SeqListInit(SeqList* psl)
{
	assert(psl);
	
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}

void SeqListDestroy(SeqList* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}

void SeqListCheckcapacity(SeqList* psl)
{
	assert(psl);
	
	//如果满了,要扩容
	if (psl->size == psl->capacity)
	{
		size_t newcapacity = 0 ? 4 : psl->capacity * 2;
		SLDateType* tmp = (SLDateType*)realloc(psl->a, newcapacity * sizeof(SLDateType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
		{
			psl->a = tmp;
			psl->capacity = newcapacity;
		}
	}
}

void SeqListPushBack(SeqList* psl,SLDateType x)
{
	assert(psl);

	//SeqListCheckcapacity(psl);
	//psl->a[psl->size] = x;
	//psl->size++;

	SeqListInsert(psl, psl->size, x);
}

void SeqListPushFront(SeqList* psl, SLDateType x)
{
	assert(psl);

	/*SeqListCheckcapacity(psl);

	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		--end;
	}
	psl->a[0] = x;
	psl->size++;*/

	SeqListInsert(psl, 0, x);
}

void SeqListInsert(SeqList* psl, size_t pos, SLDateType x)
{
	assert(psl);

	//温和检查
	if (pos > psl->size )
	{
		printf("pos 越界: %d\n", pos);
		return;
		//exit(-1)
		//return 和 exit(-1)的区别:
		//return是终止函数 , exit(-1)是直接结束程序
	}

	//暴力检查
	//assert(pos <= psl->size);

	SeqListCheckcapacity(psl);

	//int end = psl->size - 1;
	// 
	// 
 	// 这里不强制转换(int)的话,end会算术提升到pos的 unsigned int类型
	// 那么end将不会出现负数,可能循环会永远进行
	//while (end >= (int)pos) 
	//{
	//	psl->a[end + 1] = psl->a[end];
	//	--end;
	//}

	size_t end = psl->size;
	while (end > pos)
	{
		psl->a[end] = psl->a[end - 1];
		--end;
	}

	psl->a[pos] = x;
	psl->size++;
}

void SeqListErase(SeqList* psl, size_t pos)
{
	assert(psl);
	assert(pos < psl->size);

	size_t begin = pos + 1;
	while (begin < psl->size)
	{
		psl->a[begin - 1] = psl->a[begin];
		++begin;
	}

	--psl->size;
}

void SeqListPopFront(SeqList* psl)
{
	assert(psl);

	//if (psl->size > 0)
	//{
	//	int begin = 0;
	//	while (begin < psl->size - 1)
	//	{
	//		psl->a[begin] = psl->a[begin + 1];
	//		begin++;
	//	}
	//	--psl->size;
	//}

	SeqListErase(psl, 0);
}

void SeqListPopBack(SeqList* psl)
{
	assert(psl);

	//if (psl->size > 0)
	//{
	//	--psl->size;
	//}

	SeqListErase(psl, psl->size - 1);
}

int SeqListFind(SeqList* psl, SLDateType x)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

需要注意的是:

1.越界不一定能检查出来,越界是抽查,需要我们自己去检查

2.使用free函数的时候,如果在最后free这报错,其他没错误,可能是要释放的空间越界了


二、单链表

1.概念

链表是一种 物理存储结构上非连续 、非顺序的存储结构,数据元素的 逻辑顺序 是通过链表中的 指针链 次序实现的 。

?2.单链表的实现

SList.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

typedef int SLDateType;

typedef struct SListNode
{
	SLDateType date;
	struct SListNode* next;
}SListNode, SL;
//打印数据
void SListPrint(SListNode* phead);
//尾插
void SListPushBack(SListNode** phead, SLDateType x);
//头插
void SListPushFront(SListNode** phead, SLDateType x);
//尾删
void SListPopBack(SListNode** phead);
//头删
void SListPopFront(SListNode** phead);
//查找
SListNode* SListFind(SListNode* phead,SLDateType x);
//在pos位置之前插入数据
void SListInsert(SListNode** phead, SListNode* pos,SLDateType x);
//在pos位置之后插入数据
void SListInsertAfter(SListNode* pos, SLDateType x);
//删除pos位置
void SListErase(SListNode** phead, SListNode* pos);
//删除pos位置之后
void SListEraseAfter( SListNode* pos);
//销毁
void SListDestroy(SListNode** phead);

SList.c

#include "SList.h"
void SListPrint(SListNode* phead)
{
	SListNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d\n", cur->date);
		cur = cur->next;
	}
	printf("NULL\n");
}

SListNode* BuySListNode(SLDateType x)
{
	SListNode* newnode = (SLDateType*)malloc(sizeof(SLDateType));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	else
	{
		newnode->date = x;
		newnode->next = NULL;
	}
	return newnode;
}

void SListPushBack(SListNode** phead, SLDateType x)
{
	assert(phead);
	SListNode* newnode = BuySListNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		SListNode* tail = *phead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
		// 错误写法,这里没有链接起来
		//SListNode* tail = *pphead;
		//while (tail != NULL)
		//{
		//    tail = tail->next;
		//}
		//tail = newnode;  tail为NULL,上一个tail->next还是NULL
	}
}

void SListPushFront(SListNode** phead, SLDateType x)
{
	assert(phead);
	//不用检查链表头,是否为空,为空照样插入
	SListNode* newnode = BuySListNode(x);
	newnode->next = *phead;
	*phead = newnode;
}

void SListPopBack(SListNode** phead)
{
	assert(phead);
	//phead 可能为空,*phead也可能为空,要考虑两种情况;
	//该链表为空链表时,*phead = Slist = NULL

	//可以暴力检查为空的情况
	//assert(*phead != NULL)

	//链表有三种情况
	//1.空链表,无节点
	//2.一个节点
	//3.多个节点
	if (*phead == NULL)//温柔检查
	{
		return;
	}
	else if((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		SListNode* prev = *phead;
		SListNode* tail = *phead;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);//将原链尾空间释放
		tail = NULL;//将原链尾地址制空
		prev->next = NULL;//将链尾的地址制空
	}
}

void SListPopFront(SListNode** phead)
{
	assert(phead);
	if (*phead == NULL)
	{
		return;
	}
	else
	{
		SListNode* next = (*phead)->next;
		free(*phead);
		*phead = next;
	}
}

SListNode* SListFind(SListNode* phead,SLDateType x)
{
	SListNode* cur = phead;
	while (cur != NULL)
	{
		if(cur->date == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void SListInsert(SListNode** phead, SListNode* pos, SLDateType x)
{
	assert(phead);
	assert(pos);
	if (pos == *phead)
	{
		SListPushFront(phead, x);
	}
	else
	{
		SListNode* prev = *phead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SListNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

void SListInsertAfter(SListNode* pos, SLDateType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SListErase(SListNode** phead, SListNode* pos)
{
	assert(phead);
	assert(pos);
	
	if (*phead == pos)
	{
		SListPopFront(*phead);
	}
	else
	{
		SListNode* prev = *phead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

void SListEraseAfter( SListNode* pos)
{
	assert(pos);
	SListNode* next = pos->next;
	if (next)
	{
		pos->next = next->next;
		free(next);
		next = NULL;
	}
}

void SListDestroy(SListNode** phead)
{
	assert(phead);
	SListNode* cur = *phead;
	while (cur)
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*phead = NULL;
}

三、双向链表

1.双向链表的实现

List.h

#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;
//链表打印
void ListPrint(LTNode* phead);
//初始化链表
//void ListInit(LTNode** phead);
//改良后:
LTNode* ListInit();
//创建单个结点
LTNode* BuyLTNode(LTDataType x);
//尾插
void PushBack(LTNode* phead, LTDataType x);
//尾删
void PopBack(LTNode* phead);
//头插
void PushFront(LTNode* phead, LTDataType x);
//头删
void PopFront(LTNode* phead);
//在pos位置前面插入数据
void ListInsert(LTNode* pos, LTDataType x);
//删除pos位置
void ListErase(LTNode* pos);
//查找x的位置
LTNode* ListFind(LTNode* phead, LTDataType x);
//销毁
void ListDestroy(LTNode* phead);

List.c

#include "List.h"

LTNode* BuyLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

void ListPrint(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next ;
	while (cur != phead)
	{
		printf("%d", cur->data);
		cur = cur->next;
	}
	printf("\n\n");
}

//void ListInit(LTNode** phead)
//{
//	assert(phead);
//	*phead = BuyLTNode(0);
//	(*phead)->next = *phead;
//	(*phead)->prev = *phead;
//}
//改良后:
LTNode* ListInit()
{
	LTNode* phead = BuyLTNode(0);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

void PushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	//LTNode* tail = phead->prev;
	//LTNode* newnode = BuyLTNode(x);
	//tail->next = newnode;
	//newnode->prev = tail;
	//newnode->next = phead;
	//phead->prev = newnode;
	ListInsert(phead, x);//因为插入是在pos的前面,所以此处传链头位置,即插入数据在链尾
}

void PopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	/*LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	free(tail);
	tail = NULL;
	tailPrev->next = phead;
	phead->prev = tailPrev;*/
	ListErase(phead->prev);
}

void PushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	ListInsert(phead->next , x);
}

void PopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	ListErase(phead->next);
}

void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* newnode = BuyLTNode(x);
	LTNode* posPrev = pos->prev;

	pos->prev = newnode;
	newnode->next = pos;
	posPrev->next = newnode;
	newnode->prev = posPrev;
}

void ListErase(LTNode* pos)
{
	assert(pos);

	LTNode* posPrev = pos->prev;
	LTNode* posNext = pos->next;

	free(pos);
	pos = NULL;

	posPrev->next = posNext;
	posNext->prev = posPrev;
}

LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void ListDestroy(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* Next = cur->next;
		free(cur);
		cur = Next;
	}
	free(phead);
}

四、顺序表和链表的区别

不同点顺序表链表
存储空间上
物理上一定连续
逻辑上连续,但物理上不一定连续
随机访问
支持 O(1)
不支持: O(N)
任意位置插入或者删除元素
可能需要搬移元素,效率低 O(N)
只需修改指针指向
插入
动态顺序表,空间不够时需要扩容
没有容量的概念
应用场景
元素高效存储 + 频繁访问
任意位置插入和删除频繁
缓存利用率

顺序表、链表的优缺点

顺序表优点:

1.物理空间是连续的,方便用下标随机访问。

2.CPU高速缓存命中率会更高(了解即可)

顺序表缺点:

1.由于需要物理空间连续,空间不够需要扩容。扩容本身有一定消耗。其次扩容机制还存在一定的空间浪费。

2.头部或者中部删除插入,挪动数据,效率低。O(N)

链表优点:

1.任意位置插入删除效率高。O(1)

2.按需申请和释放空间

链表缺点:

1.不支持下标的随机访问,有些算法不适合在链表上进行。如:二分查找,排序等

总的来说,这两个结构是相辅相成的结构


  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:38:43  更:2022-04-01 23:38:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:35:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码