数据结构与算法:顺序表和链表的常用操作 (很全,基本涵盖本章所有算法)


前言:

数据结构中的表操作算是里面较为简单的章节,但也比较重要,3月份学完的此章节,左思右想今天还是写篇 关于顺序表和链表的博客。 ps : 复制代码可以在编译器上直接运行。

一、顺序表

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

  • 顺序表一般可以分为:

      静态顺序表:使用定长数组存储。
      动态顺序表:使用动态开辟的数组存储。
    

在这里插入图片描述


#define N 100 typedef int SLDataType;
 
typedef struct SeqList {   
		  SLDataType array[N]; // 定长数组 
		  size_t size;        // 有效数据的个数   
}SeqList;
// 顺序表的动态存储
 typedef struct SeqList {
     		 SLDataType* array;  // 指向动态开辟的数组
       	     size_t size ;       // 有效数据个数
             size_t capicity ;   // 容量空间的大小
   }SeqList;


顺序表的操作:

InitList_Sq(&L); 初始化函数
InputList_Sq(&L); 数据的输入
InsertList_Sq(&L,n,num); 数据节点插入
ListDelet_Sq(&L,n,&e); 删除数据节点
LocateElem_Sq(&L,num); 顺序表的查找
GetElem_Sq(&L,n,&e) ; 顺序表的读取

1、顺序表主函数:

int main()
{
	int n,num,e,value;
	SqList L;
	
	/*顺序表初始化和输入*/
	value=InitList_Sq(&L);
	if(value)
	{
		printf("线性表初始化成功!\n");
	}
	else
	{
		printf("线性表初始化失败!\n");
		return ERROR; 
	}
	InputList_Sq(&L);
	printf("顺序表各元素的值为:"); 
	Display_Sq(&L);	

	
	/*顺序表插入*/
	printf("\n请输入插入位置:"); 
	scanf("%d",&n);
	printf("请输入插入元素:");
    scanf("%d",&num); 
	value=InsertList_Sq(&L,n,num);
	if(value)
	{
		printf("进行插入操作后线性表依次为:\n");
		Display_Sq(&L);	
	}
	else
	{
		printf("插入失败!");
		return ERROR;
	} 
	
   /*顺序表删除*/
	printf("\n请输入删除位置:");
	scanf("%d",&n);
	value=ListDelet_Sq(&L,n,&e);
	if(value)
	{
		printf("被删除的元素为:%d\n",e);
		printf("进行删除操作后线性表依次为:\n");
		Display_Sq(&L);	
	}
	else
	{
		printf("删除失败!\n");
		return ERROR;
	} 
	
	/*顺序表查找*/
	printf("\n请输入要查找的元素值:");
	scanf("%d",&num);
	value=LocateElem_Sq(&L,num);
	if(value>=0) printf("该元素在第%d位\n",value+1);
	else  printf("error!\n");
	
	/*顺序表读取*/
	printf("\n请输入读取元素的位置:");
	scanf("%d",&n); 
	value=GetElem_Sq(&L,n,&e);
	if(value) printf("该元素为:%d\n",e);
	else printf("error!\n");

	return 0;
}

2、顺序存储的线性表抽象数据类型定义:

#include<stdio.h>
#include<stdlib.h>

#define  INIT_SIZE   100  /*线性表初始长度分配量*/ 
#define  INCREMENT   10   /* 线性表存储分配量增量*/
#define  OK     1
#define  ERROR  0
typedef  int  Elemtype;//数据类型重定义 
typedef  int  Status;//状态类型重定义 
typedef struct{
	Elemtype *elem;//元素存储空间基地址 
	int length;//表的当前长度 
	int listsize;//表的初始分配存储容量 
}SqList;

3、顺序表的初始化

Status InitList_Sq(SqList *L)//线性表的初始化 
{
	L->elem=(Elemtype*)malloc(INIT_SIZE*sizeof(Elemtype));
	//给表分配初始元素存储容量 
	if(!L->elem)//异常判断 
	{
		return ERROR;
	}
	L->length=0;//初始化表的当前长度为0 
	L->listsize=INIT_SIZE;//初始化表的分配存储容量为 INIT_SIZE  
	
	return OK; 
}

4、顺序表数据输入:

void InputList_Sq(SqList *L)
{
	int i,n;
	printf("输入顺序表的长度:");
	scanf("%d",&n);
	while(n>L->listsize)
	{
		printf("超出存储空间!重新输入:");
		scanf("%d",&n);
	}
	L->length=n;
	for(i=0;i<n;i++)
	{
		printf("请输入第%d个元素的值:",i); 
		scanf("%d",&L->elem[i]);
	}
}

5、顺序表的插入 (用 realloc , 程序执行完会自动释放空间)

/*顺序表L的插入,在第i个位置前插入新的元素e,成功返回1,否则返回0*/
Status InsertList_Sq(SqList *L,int i, Elemtype e)
{
	int j;
	Elemtype *NewSpace;
	//插入位置不合法 
	if(i<1||i>L->length)
	{
		return ERROR;
	}
	
	if(L->length>=L->listsize)  
	{   
	    /* realloc扩容后,系统会自动释放掉L->elem存储空间*/ 
		NewSpace=(Elemtype*)realloc(L->elem,(L->listsize+INCREMENT)*sizeof(Elemtype));
		if(!NewSpace)                                    //分配失败
		{                                       
			return ERROR;
		}
		L->elem=NewSpace;                               //获得新的内存分配 
		L->listsize+=INCREMENT;                        //重分配后的存储容量 
	}
	
	for(j=L->length;j>=i;j--) //i位后的元素往后移动
	{
		L->elem[j]=L->elem[j-1];
	}
	L->elem[j]=e;//插入操作
	L->length++;//表长加一
	return OK;
} 

6、顺序表的删除:

/*顺序表删除操作,删除表中第i位的元素,并返回删除删除元素的值*/ 
Status ListDelet_Sq(SqList *L,int i, Elemtype *e)
{
	int j;
	if(i<1||i>L->length)//删除位置不存在 
	{
		return ERROR;
	}
	*e=L->elem[i-1];//返回删除元素的值 
	for(j=i;j<L->length;j++)//删除操作 
	{
		L->elem[j-1]=L->elem[j];
	}
	L->length--;//表长减一
	return OK;
}


7、顺序表的按值查找:

/*顺序表按值查找,成功返回它的序位,否则返回-1*/ 
Status LocateElem_Sq(SqList *L,Elemtype e)
{
	int i;
	for(i=0;i<L->length;i++)
	{
		if(L->elem[i]==e)
		return i;
	}
	return -1;
} 

8、顺序表元素读取:

/*读取顺序表的元素*/
Status GetElem_Sq(SqList *L,int i,Elemtype *e)
{
	if(i<1||i>L->length)
	{
		return ERROR;
	}
	*e=L->elem[i-1];
	return OK;
}

9、顺序表输出函数:

//顺序表的输出函数 
void Display_Sq(SqList *L) 
{
	int i;
	for(i=0;i<L->length;i++)
	{
		printf("%d ",L->elem[i]);
	}
	printf("\n");
}


二、链表

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

在这里插入图片描述
在这里插入图片描述
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

单向 、 双向
带头 、 不带头
循环 、 非循环
1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结 构,如哈希桶、图的邻接表等等。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而 简单了,后面我们代码实现了就知道了。

双向链表

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

双向循环链表
在这里插入图片描述


链表的操作

// 初始化 ,构造一条空的链表
void SListInit(SListNode **ppFirst);
//打印链表
void SListInit(SListNode ppFirst);
// 尾部插入
void SListPushBack(SListNode
ppFirst, DataType data);
// 头部插入
void SListPushFront(SListNode **ppFirst, DataType data);
// 尾部删除
void SListPopBack(SListNode **ppFirst);
// 头部删除
void SListPopFront(SListNode **ppFirst);
// 给定结点插入,插入到结点前
void SListInsert(SListNode **ppFirst, SListNode *pPos, DataType data);
// 给定结点删除
void SListErase(SListNode **ppFirst, SListNode *pPos);
// 按值删除,只删遇到的第一个
void SListRemove(SListNode **ppFirst, DataType data);
// 按值删除,删除所有的
void SListRemoveAll(SListNode **ppFirst, DataType data);
// 销毁 ,需要销毁每一个节点
void SListDestroy(SListNode **ppFirst);
// 按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL
int SListFind(SListNode *pFirst, DataType data);


1. 链表的初始化

  • 创建一个单链表 :
//不是链表的结构体
//链表中一个节点的结构体
typedef struct SListNode {
	DataType data; // 值 
	struct SListNode *Next; // 指向下一个结点 
} SListNode;
  • 初始化:
void SListInit(SListNode **ppFirst)//初始化
{
	assert(ppFirst != NULL);
	*ppFirst = NULL;
}

2、打印链表

//打印链表
void SListPrint(SListNode *First)
{
	for (SListNode*cur = First; cur != NULL; cur = cur->Next)
	{
		printf("%d-->", cur->data);
	}
	printf("\n");
	return 0;
}

3、尾插法

在这里插入图片描述

//申请新节点
static SListNode* CreateNode(DataType data)
{
	SListNode*node = (SListNode*)malloc(sizeof(SListNode));
	node->data = data;
	node->Next = NULL;
	return node;
}
// 尾部插入 
void SListPushBack(SListNode** ppFirst, DataType data)
{
	assert(ppFirst != NULL);
	SListNode *node = CreateNode(data);
	if (*ppFirst == NULL)//判断链表不为空
	{
		*ppFirst = node;
		return;
	}
	//找链表中的最后一个节点
	SListNode* cur = *ppFirst;
	while (cur->Next != NULL)
	{
		cur = cur->Next;
	}
	cur->Next = node;//插入新申请的节点
}

4、头插法

在这里插入图片描述

void SListPushFront(SListNode **ppFirst, DataType data)
{
	assert(ppFirst != NULL);
	SListNode *node = CreateNode(data);
	node->Next = *ppFirst;
	*ppFirst = node;
}

5、尾删法
在这里插入图片描述

// 尾部删除 
void SListPopBack(SListNode **ppFirst)
{
	assert(ppFirst != NULL);
	assert(*ppFirst != NULL);
	if ((*ppFirst)->Next == NULL)
	{
		free(*ppFirst);
		*ppFirst = NULL;
		return;
	}
	SListNode*cur = *ppFirst;
	while (cur->Next->Next != NULL)
	{
		cur = cur->Next;
	}
	free(cur->Next);
	cur->Next = NULL;
}

6、头删法

在这里插入图片描述

// 头部删除 
void SListPopFront(SListNode **ppFirst)
{
	assert(ppFirst != NULL);
	assert(*ppFirst != NULL);//链表不是空链表
	SListNode *first = *ppFirst;
	*ppFirst = (*ppFirst)->Next;
	free(first);
}

7、给定结点插入,插入到结点前

在这里插入图片描述

void SListInsert(SListNode **ppFirst, SListNode *pPos, DataType data)
{
	assert(ppFirst != NULL);
	if (*ppFirst == pPos)
	{
		SListPushFront(ppFirst, data);
		return;
	}
	SListNode*newNode = CreateNode(data);
	SListNode*cur;
	//找到pos前的一个节点
	for (cur = *ppFirst; cur->Next != pPos; cur = cur->Next){ }
	//改变的是字段内的值,而不是指针的值
	cur->Next = newNode;
	newNode->Next = pPos;
}

8、给定结点删除
在这里插入图片描述

// 给定结点删除 
void SListErase(SListNode **ppFirst, SListNode *pPos)
{
	if (*ppFirst == pPos)
	{
		SListPopFront(ppFirst);
		return;
	}
	SListNode *cur = *ppFirst;
	while (cur->Next != pPos)
	{
		cur = cur->Next;
	}
	cur->Next = pPos->Next;
	free(pPos);
}

9、按值删除,只删遇到的第一个

// 按值删除,只删遇到的第一个 
void SListRemove(SListNode **ppFirst, DataType data)
{
	SListNode *cur = *ppFirst;
	SListNode *prev = NULL;
	assert(ppFirst != NULL);
	if (*ppFirst == NULL)
		return;
	while (cur)
	{
		if (cur->data == data)
		{
			//删除
			//1.删除的是第一个节点
			if (*ppFirst == cur)
			{
				*ppFirst = cur->Next;
				free(cur);
				cur = NULL;
			}
			else//删除中间节点
			{
				prev->Next = cur->Next;
				free(cur);
				cur = NULL;
			}
			break;
		}
		prev = cur;
		cur = cur->Next;
	}
}

10、按值删除,删除所有的

// 按值删除,删除所有的 
void SListRemoveAll(SListNode **ppFirst, DataType data)
{
	SListNode *cur = NULL;
	SListNode *prev = NULL;
	assert(ppFirst != NULL);
	if (*ppFirst == NULL)
		return;
	cur = *ppFirst;
	while (cur)
	{
		if (cur->data == data)
		{
			//删除
			//1.删除的是第一个节点
			if (*ppFirst == cur)
			{
				*ppFirst = cur->Next;
				free(cur);
				cur = *ppFirst;
			}
			else//删除中间节点
			{
				prev->Next = cur->Next;
				free(cur);
				cur = prev;
			}
		}
		prev = cur;
		cur = cur->Next;
	}
}


11、 销毁 ,需要销毁每一个节点

// 销毁 ,需要销毁每一个节点
void SListDestroy(SListNode **ppFirst)
{
	SListNode*cur = NULL;
	SListNode*del = NULL;
	assert(ppFirst);
	cur = *ppFirst;
	while (cur)
	{
		del = cur;
		cur = cur->Next;
		free(del);
		del = NULL;
	}
	*ppFirst = NULL;
}

12、按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL

// 按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL 
SListNode *  SListFind(SListNode *pFirst, DataType data)
{
	for (SListNode *cur = pFirst; cur != NULL; cur = cur->Next)
	{
		if (cur->data == data)
		{
			return cur;
		}
	}
	return NULL;
}

猜你喜欢

转载自blog.csdn.net/haduwi/article/details/106914849