顺序表 单链表、双向带头循环链表

1、双向带头循环链表

线性表=有n个元素构成的集合,逻辑结构呈现线性。
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势;增加头结点的目的是算法实现上的方便:在头部进行插入 删除时,可以和其他节点写法一样,统一起来,不用判断是否为链表第一个节点。
head不是存放有效数据的节点,如果只有一个head节点,说明链表为空。
在这里插入图片描述
在这里插入图片描述
带头结点的双循环链表:末尾插入结点和删除尾结点最节省时间,找尾:头节点的下一个;单链表需要找到前驱节点,然后才能删除尾,找尾:一个一个往后走:耗时
在这里插入图片描述

List.c

#include "List.h"
//理解不了则写代码,代码写多了则理解,看懂之后再不看别人写的自己“默写”
//双向带头循环链表
//方法1:
void ListPushBack(ListNode*phead, LTDataType x){
	assert(phead);
	ListNode*tail = phead->prev;
	ListNode*newnode = BuyListNode(x);
	//把3个节点(phead\tail\newnode)链接起来 
	tail->next = newnode; newnode->prev = tail;
	newnode->next = phead; phead->prev = newnode;
}
//方法2:
void ListPushBack(ListNode*phead, LTDataType x){
	ListInsert(phead, x); //尾插:phead的前1个=尾
}
void ListPopBack(ListNode* phead){
	//方法2:
	assert(phead);
	assert(phead->next != phead);
	ListNode*tail = phead->prev;//tail局部变量
	ListNode*tailPrev = tail->prev;
	tailPrev->next = phead; phead->prev = tailPrev;
	free(tail); tail = NULL;
	//方法1:
	ListErase(phead->prev);
}
void ListPushFront(ListNode* phead, LTDataType x){
	//方法2:
	assert(phead);//头插=第1个有效数字前面插入
		ListNode*first = phead->next;
	ListNode*newnode = BuyListNode(x);
	phead->next = newnode; newnode->prev = phead;
	newnode->next = first; first->prev = newnode;
	//方法1:
	ListInsert(phead->next,x);
}
void ListPopFront(ListNode* phead){
	//方法2:
	assert(phead);
	assert(phead->next != phead);//没有节点了,再删把头节点都删了
	ListNode*first = phead->next;
	ListNode*second = first->next;
	phead->next = second; second->prev = phead;
	free(first); 
	//方法1:
	ListErase(phead->next);
}
ListNode*ListFind(ListNode* phead, LTDataType x){
	assert(phead);
	ListNode*cur = phead->next;
		while (cur!=phead){
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
	}
void ListInsert(ListNode* pos, LTDataType x){
	assert(pos);//3个(posprev newnode pos)之间链接,pos的前面插入
		ListNode*posPrev = pos->prev;
		ListNode*newnode = BuyListNode(x);
			posPrev->next = newnode;
		newnode->prev = posPrev;
		newnode->next = pos;
		pos->prev = newnode;
}
void ListErase(ListNode* pos){
	assert(pos);
	//assert(pos!=phead);//不允许删除头
		ListNode*posPrev = pos->prev;
	ListNode*posNext = pos->next;
	free(pos);
	//posPrev pos posNext 3个节点
	posPrev->next = posNext;
	posNext->prev = posPrev;
	}
ListNode* BuyListNode(LTDataType x){
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	//不=空则为随机值;除了静态变量、全局变量,如果不初始化,则为随机值
	return node;
}
void SListPrint(ListNode* phead){
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead){//循环链表cur永远不会=空,cur = phead时,即遍历完所有节点
		printf("%d->", cur->data);
		cur = cur->next;
	}printf("\n");}
	void ListInit(ListNode** pphead){//方法1:是指针,则要传指针phead的地址才能改变
		*pphead = BuyListNode(0);
		(*pphead)->next = *pphead;
		(*pphead)->prev = *pphead;
	}
//方法2:返回1个指针
ListNode*ListInit(){
		ListNode*phead = BuyListNode(0);
		(phead)->next = phead;
		(phead)->prev =phead;
		return phead;
	}
void ListClear(ListNode*phead){
//把插入的数字都删除,保留头结点,可以继续使用,准备输入新数字,传1级指针
			assert(phead);
		ListNode*cur = phead;
		while (cur!=phead){
			ListNode*next=cur->next;
			free(cur); cur = next;
		}
		(phead)->next = phead; //链表永远要保持带头双向循环
		(phead)->prev = phead;
		}
void ListDeatory(ListNode**pphead){//销毁,传2级指针
	assert(*pphead);
	ListClear(*pphead);//清除数据
		free(*pphead); //清除头
		*pphead = NULL;
	}

List.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode*next;
struct ListNode*prev;
LTDataType data;
}ListNode;

ListNode* BuyListNode(LTDataType x);
void ListPrint(ListNode* phead);
ListNode*ListInit();
 void ListClear(ListNode* phead);
 void ListDestory(ListNode** pphead);
 void ListPushBack(ListNode*phead, LTDataType x);
 void ListPopBack(ListNode* phead);
 void ListPushFront(ListNode*phead, LTDataType x);
 void ListPopFront(ListNode* phead);
 ListNode*ListFind(ListNode* phead, LTDataType x);
 void ListInsert(ListNode* pos, LTDataType x); //在pos前面插入X
void ListErase(ListNode* pos);

test.c

#include"List.h"
void test1(){
ListNode*phead = ListInit();
	ListPushBack(phead, 1);
	ListPushBack(phead, 2);
	ListPrint( phead);
	ListPopBack( phead);
	ListPrint(phead);
	ListPushFront(phead,3);
	ListPrint(phead);
	ListPopFront(phead);
	ListPrint(phead);
	ListDestory(& phead);
}
void test2(){
	ListNode*phead = ListInit();
	ListPushBack(phead, 1);
	ListPushBack(phead, 2);
	ListPrint(phead);
	ListNode*pos = ListFind(phead, 2);
	pos->data = 300;//把2改为300
	ListInsert(pos, 30);//在2的前面插入30
	ListPrint(phead);
	ListErase(pos);
	ListPrint(phead);
	ListDestory(&phead);
}
int main()
{test1();
		return 0;}

2、链表和顺序表(数组)的区别和联系:

顺序表

就是在数组的基础上实现增删查改,并且插入时可以动态增长
顺序表缺陷:
a、可能存在一定空间浪费,增容一般是呈2倍的增长
b、增容有一些效率损失,需要开辟空间、释放空间
c、中间或者头部插入删除,时间复杂度为O(N),因为要挪动数据。
这些问题链表来解决。
线性表=有n个元素构成的集合,逻辑结构呈现线性
栈一般用顺序表实现,栈只能在一端插入和删除数据

链表

优点:用1个,则申请1个;
2.现实中的结点一般都是从堆上申请出来的
3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续
队列一般用链表实现。链栈,一般需要进行头插(头删)操作,而顺序栈一般进行尾插(头删)操作。

链表和顺序表(数组)

是互补的数据结构,链表是因为数组的缺陷(定长的)而产生的,
在这里插入图片描述

3、单链表

无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

1
在这里插入图片描述
2
在这里插入图片描述

3在这里插入图片描述

test.c

无环

#include"SList.h"
int main()
{
	SListNode* pList=NULL;//头指针
	SListPushBack(&pList, 1);
	SListPushBack(&pList, 2);
	
	//SListPopBack(&pList); //有问题,为什么
	SListPushFront(&pList, 3);
	SListPushFront(&pList, 4);
	SListPopFront(&pList);
	SListNode*pos = SListFind(pList,3);//把3改为30,数组给的是下标,链表给的是节点指针=地址
	if (pos){
		pos->data = 30;
	}
		SListInsertAfter(SListFind(pList,2), 6);
	SListEraseAfter(SListFind(pList, 3));

	SListPrint(pList); SListDestroy(&pList);
	SListReversePrint( pList);
	/*方法1时:
	ListNode*phead = NULL;
	ListInit(&phead);*/
			return 0;
	}

有环

SListNode* HasCircle(SListNode* L)
{
	SListNode* fast = L; SListNode* slow = L;
	while (fast && fast->next)
	{
		fast = fast->next->next; //1次走2步
		slow = slow->next;
		if (fast == slow)
			return fast;//带环
	}return NULL;//不带环
}
SListNode* CheckListCross(SListNode*, SListNode* L2)//返回交点
{
	SListNode* pM1 = HasCircle(L1);
	SListNode* pM2 = HasCircle(L2);
	if (NULL == pM1 && NULL == pM2)
	{
		// 两个链表都不带环
	}
	else if (pM1 & & pM2){
		//两个链表都带环
	}return NULL;
}
void TestsList2()
{
	SListNode* pList1 = NULL;
	SListPushBack(&pList1, 1);
	SListPushBack(&pList1, 2);
	SListPushBack(&pList1, 3);
	SListPushBack(&pList1, 4);
	SListFind(pList1, 4)->next = SListFind(pList1, 2);//带环链表
	
	SListNode* pList2 = NULL;
	SListPushBack(&pList2, 11);
	SListPushBack(&pList2, 22);
	SListPushBack(&pList2, 33);
	SListFind(pList2, 33)->next = SListFind(pList1, 3);//交点在环外

}

SList.h

//声明
#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef int SListDataType;
typedef struct SListNode
{
	SListDataType data; 
	struct SListNode*next;//指向下一个节点的4字节指针变量
}SListNode;                 //SListNode=SLTNode,cur++=+1个结构体大小

SListNode* BuySListNode(SListDataType x);
void SListPushBack(SListNode** phead, SListDataType x);
 void SListPopBack(SListNode** phead);
 void SListPushFront(SListNode** phead,SListDataType x);
 void SListPopFront(SListNode** phead);
 SListNode*SListFind(SListNode* phead, SListDataType x);
 void SListPrint(SListNode* phead);//不改变外面指针,不需要2级指针,当然也可以使用2级指针
 void SListInsertAfter(SListNode* pos, SListDataType x);

// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);

void SListReversePrint(SListNode* plist);
void SListDestroy(SListNode** plist);

void TestSList1();

SList.c

#include "SList.h"
#include <malloc.h>
#include <assert.h>
#include <stdio.h>

SListNode* BuySListNode(SListDataType x)
{
	SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
	if (newNode == NULL)
	{
		printf("申请结点失败\n");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;//不值空则为随机值

	return newNode;
}

void SListPushBack(SListNode** pphead, SListDataType x)
{//传指针的地址,2级指针
	SListNode* newNode = BuySListNode(x);

	if (*pphead == NULL)//链表=空
	{
		*pphead = newNode;//*解引用
	}
	else
	{
		 //找尾,尾的next是空,->解引用
		SListNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newNode;
	}
}

void SListPopBack(SListNode** pphead)
{
	// 1、空
	// 2、一个结点
	// 3、一个以上结点
	if (*pphead == NULL)
	{
		return;//必须先判断*pphead = NULL,否则如果头=空,(*pphead)->next == NULL判断时=空的next的判断=崩
	}
	else if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SListNode* prev = NULL;
		SListNode* tail = *pphead;//tail局部变量,1个节点以上节点
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}

		free(tail);
		prev->next = NULL;
	}
}

void SListPushFront(SListNode** pphead, SListDataType x)//头插
{
	SListNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

void SListPopFront(SListNode** pphead)
{//传plist的地址过来,* pphead=plist的值=第1个节点的地址;1空2一个节点3一个以上节点
	// 1.空
	// 2.一个节点 + 3.一个以上的节点 
	if (*pphead == NULL)
	{
		return;//如果是空则不删
	}
	else
	{
		SListNode* next = (*pphead)->next;
		free(*pphead);//free释放:指针指向的内存,不再拥有使用权,释放空间并且置为随机值;内存泄露:指针丢了

		*pphead = next;
	}
}//合并多个节点+1个节点的情况

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

// 单链表查找
SListNode* SListFind(SListNode* phead, SListDataType x)
{
	SListNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;//没有找到则返回空
}

void SListInsertAfter(SListNode* pos, SListDataType x)
{
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SListEraseAfter(SListNode* pos)//删除pos位置之后的值
{
	assert(pos);
	if (pos->next)
	{
		SListNode* next = pos->next;
		SListNode* nextnext = next->next;
		pos->next = nextnext;
		free(next);
	}
}//逆序打印链表 方法1:
void SListReversePrint(SListNode* plist){
	if (NULL != plist){
		SListReversePrint(plist->next);
		printf("%d ", plist->data);
	}
}
//方法2

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

逆序打印链表

4、顺序表

方法2:运用栈

seqList.C

#include "seqList.h"
#include <stdio.h>
#include <malloc.h>
#include <assert.h>

//将ps指向的结构体变量初始化成功
void SeqListInit(SeqList* ps, int initCapacity){
assert(ps) ;
ps->array = (int* ) malloc(initCapacity*sizeof (int) ) ;
if(NULL==ps->array){
assert (0) ;
return;
}
ps->capacity = initCapacity;ps->size = 0 ;}

void seqListDestroy (SeqList* ps){
assert (ps);if (ps->array){
free(ps->array) ;ps->array = NULL;ps->capacity =0;
ps->size=0;}}
void seqListPrint (seqList* ps){
assert (ps) ;
for (int i=0;i<ps->size;++i){
printf ( "%d ", ps->array[i] );
}}
int seqListEmpty (SeqList* ps){
assert (ps) ;
return 0== ps->size;}//是空:返回真
void seqListPushBack (SeqList* ps,DataType data){//只要是插入都检测空间是否足够
assert(ps) ;ChecKCapacity(&ps);
ps->array[ps->size] =data;ps->size++;}
void seqListPopBack(seqList*ps){
assert (ps) ;
//如果顺序是空的---直接返回
if (SeqListEmpty (ps)) {
return;}
ps->size--;}
int seqListsize (seqList* ps){
assert(ps) ;
return ps->size;
}
//空间总的大小:
int seqListCapacity (segList* ps){
assert(ps) ;
return ps->capacity;}
int seqListFind (seqList* ps,DataType data){
assert(ps) ;
  for (int i = 0; i< ps->size; ++i){
  if(ps->array [i]==data){
   return i;
     }
   }return -1;
}
void seqListPushFront(SeqList*ps, DataType data){//时间复杂度=O(N)
assert (ps) ;ChecKCapacity(&ps);
//将顺序表中所有元素整体往后搬移一个位置
for(int i=ps->size - 1; i >=0 ; --i)
{ps->array [i+1]=ps->array [i ];
}
//3.插入元素
ps->array [0] = data;
ps->size++;
}
void seqListPopFront (seqList* ps){
if (SeqListEmpty (ps)){
return;
)
//将顺序表中的元素除第一个外整体往前搬移一步
for (int i = 0; i < ps->size - 1; i++){
ps->array [i] =ps->array [i+1];
}
ps->size--;}
void SeqListInsert(SeqList* ps, size_t pos, DataType data){
	assert(ps);
	if (pos < 0 || pos > ps->size)
	{
		return;
	}ChecKCapacity(&ps);
	for (int i = ps->size - 1; i >= pos; i--){
		ps->array[i+1] = ps->array[i];
	}
	ps->array[pos] = data; ps->size++;
}
void seqListErase(SeqList* ps, size_t pos){
	assert(ps);
	if (pos < 0 || pos >= ps->size){
		printf("位置有问题\n"); return;
	}
	for (int i = pos + l; i < ps->size; ++i){
		ps->array[i - 1] = ps->array[i];
	}
	ps->size--;
}
//获取顺序表中最后一个元素
DataType seqListBack (SeqList* ps){
assert(ps) ;
return ps->array[ps->size-1];}

void ChecKCapacity(seqList* ps){
	assert(ps);
	if (ps->size == ps->capacity)
	{
		int newCapacity = (ps->capacity << 1);
		DataType* temp = (DataType*)malloc(sizeof (DataType)*newCapacity);
		if (NULL == temp)
		{
			return;
		}
		for (int i = 0; i < ps->size; ++i){
			temp[i] = ps->array[i];
		}
		free(ps->array);
		ps->array = temp;
		ps->capacity = newCapacity;
	}
}


void TestseqList1({
SeqList s;
SeqListInit ( &s,10) ;
 seqListInit ( &s,10);seqListPushBack ( &s,1);
SeqListDestroy ( &s) ;

}
//测试顺序表
void TestSeqList (){
TestSeqList1 ();
}

seqList.h

typedef int DataType;
//动态顺序表:底层的空间从堆上动态申请出来的
typedef struct SeqList{
int* array;//指向存储元素空间的起始位置
int capacity;//表示空间总的大小
int size;//有效元素的个数
} seqList;
void seqListInit (SeqList* ps, int initCapacity);
void seqListDestroy (seqList* ps);I
void SeqListPrint (SeqList* ps) ;
void TestSeqList ( );

主函数:

include <stdio.h>
#include "SeqList.h"
int main ( )
{
TestSeqList ( );return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值