顺序表+链表(手撕)

一、线性表

  1. 概念:n个具有相同特性的数据元素的有限序列。

  2. 特点:逻辑上是线性的,但物理结构上不一定是连续的。

  3. 分类:物理存储时,通常以数组和链式结构的形式存储。

二、顺序表

一段物理地址连续的存储单元,依次存储数据元素的线性结构

只能从头部开始 连续存储

  1. 静态顺序表:使用定长数组存储元素
  2. 动态顺序表:使用动态开辟的数组存储
typedef int SLDataType;//类型重定义

typedef struct SeqList//类型重定义
{
    SLDataType* a;
    int size;      //有效数据
    int caoacity;  //空间容量
}SL;

// SeqList.c
#include "SeqList.h"

void SeqListInit(SeqList* ps)
{
	assert(ps);

	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

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

void SeqListPrint(SeqList* ps)
{
	assert(ps);

	for (inti = 0; i < ps->size; ++i)
	{
		printf("%d ", ps->a[i]);
	}

	printf("%\n");
}

void CheckCacpity(SeqList* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ps->a = (SLDateType*)realloc(ps->a, newcapacity*sizeof(SLDateType));
		ps->capacity = newcapacity;
	}
}

// 以下几个接口先讲不复用Insert和Erase的实现,最后再讲复用实现
void SeqListPushBack(SeqList* ps, SLDateType x)
{
	//assert(ps);
	//CheckCacpity(ps);

	//ps->a[ps->size] = x;
	//ps->size++;

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

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

	/*CheckCacpity(ps);

	int end = ps->size;
	while (end > 0)
	{
		ps->a[end] = ps->a[end - 1];
		--end;
	}

	ps->a[0] = x;
	++ps->size;*/

	SeqListInsert(ps, 0, x);
}

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

	//int start = 0;
	//while (start < ps->size-1)
	//{
	//	ps->a[start] = ps->a[start + 1];
	//	++start;
	//}
	//int start = 1;
	//while (start < ps->size)
	//{
	//	ps->a[start-1] = ps->a[start];
	//	++start;
	//}

	//--ps->size;
	SeqListErase(ps, 0);
}

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

	//ps->a[ps->size - 1] = 0;
	//ps->size--;
	SeqListErase(ps, ps->size-1);
}

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

	return -1;
}

// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, size_t pos, SLDateType x)
{
	assert(ps);
	assert(pos <= ps->size);

	CheckCacpity(ps);

	//int end = ps->size - 1;
	//while (end >= pos)
	//{
	//	ps->a[end + 1] = ps->a[end];
	//	--end;
	//}

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


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

// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos)
{
	assert(ps && pos < ps->size);

	//int start = pos;
	//while (start < ps->size-1)
	//{
	//	ps->a[start] = ps->a[start + 1];
	//	++start;
	//}

	int start = pos+1;
	while (start < ps->size)
	{
		ps->a[start-1] = ps->a[start];
		++start;
	}

	ps->size--;
}

三、链表

1、无头(无哨兵位)单向不循环链表----结构最简单的链表


#include"SList.h"
	}
	else//多个指针
	{
		SLNode* tail = *pphead;
		SLNode* prev = NULL;//尾的前一个
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;//不置空也可以 但是养成良好习惯
		prev->next = NULL;
	}
	
}

//头删
void SLTPopFront(SLNode** pphead)
{
	assert(*pphead);
	
	//一个或多个结点都能处理
	SLNode* tem = (*pphead)->next;
	free(*pphead);//free *pphead所指向的结点 
	*pphead = tem;
}


//查找
SLNode* SLTFind(SLNode* phead, SLNDataType x)
{
	while (phead->val != x)
	{
		if (phead->next == NULL)
		{
			printf("链表中没有想要的值。");
		}
		else
		{
			phead = phead->next;
		}
	}
	return phead;
}


//在pos前插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{
	assert(pphead);
	assert((pos && *pphead)|| (!pos && !(*pphead)));
	SLNode* newnode = CreateNode(x);

	if (pos != *pphead)//pos不是第一个结点
	{
		while ((*pphead)->next != pos)//找pos之前一个结点
		{
			*pphead = (*pphead)->next;
		}
		newnode->next = (*pphead)->next;
		(*pphead)->next = newnode;
	}
	else
	{
		SLNode* newnode = CreateNode(x);
		newnode->next = *pphead;
		*pphead = newnode;
	}
}

//删除pos位置
void SLTEares(SLNode** pphead, SLNode* pos)
{
	if (pos != *pphead)//pos不是第一个结点
	{
		while ((*pphead)->next != pos)//找pos之前一个结点
		{
			*pphead = (*pphead)->next;
		}
		(*pphead)->next = pos->next;
		free(pos);
		pos = NULL;
	}
	else
	{
		*pphead = pos->next;
		free(pos);
		pos = NULL;
	}
}


void SLTDestory(SLNode** pphead)
{
	assert(pphead);
	
	SLNode* cur = *pphead;
	while (cur)
	{
		SLNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;

}
 2、带头(有哨兵位)双向循环链表----结构最复杂的链表

#include"List.h"


// 双向链表打印
void ListPrint(LTNode* phead)
{
	assert(phead);
	printf("哨兵位");

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

//创建返回链表的头结点
LTNode* ListInit()
{
	LTNode* phead = CreateLTNode(-1);

	phead->next = phead;
	phead->prev = phead;

	return phead;
}

//创造新结点
LTNode* CreateLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->val = x;
	newnode->next = NULL;
	newnode->prev = NULL;

	return newnode;
}

//尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* tail = phead->prev;
	LTNode* newnode = CreateLTNode(x);

	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);//空

	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;

	free(tail);
	tailPrev->next = phead;
	phead->prev = tailPrev;
}

//头插
void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = CreateLTNode(x);
	newnode->next = phead->next;
	newnode->prev = phead;
	phead->next->prev = newnode;
	phead->next = newnode;

}

// 头删
void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);//判空

	LTNode* cur = phead->next;
	phead->next = cur->next;
	cur->next->prev = phead;
	free(cur);
	cur = NULL;
}

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

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

//在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = CreateLTNode(x);
	LTNode* pprev = pos->prev;

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


//删除pos位置的节点
void ListErase(LTNode* pos)
{
	LTNode* pprev = pos->prev;
	LTNode* pnext = pos->next;

	pprev->next = pnext;
	pnext->prev = pprev;

	free(pos);
	pos = NULL;

}

// 销毁
void ListDestory(LTNode* phead)
{
	assert(phead);

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

四 、顺序表和链表的选择

1、链表(双向为例)优缺点

优点:

  • 任意位置插入删除都是O(1)
  • 按需申请和释放空间,合理利用空间,不存在浪费

缺点:

  • 下标随机访问不方便O(n)
 2、顺虚表优缺点

优点:

  • 支持下标的随机访问O(n)物理地址连续

缺点:

  • 头部或中间插入删除时效率低,需要挪动数据O(n)
  • 空间不够需要扩容,有消耗,存在一定的空间浪费
  • 只适合尾插
3、二者区别

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值