【数据结构】线性表——顺序表 链式表 单链表 双链表 循环链表 静态链表

news2024/11/21 0:16:39

文章目录

  • 二 线性表
    • 1.定义与基本操作
    • 2.顺序表
      • 2.1 定义
      • 2.2 静态分配
    • 3.链式表
      • 3.1 单链表
        • 3.1.1 定义
        • 3.1.2 建立
          • 1)头插法建立单链表
          • 2)尾插法建立单链表
        • 3.1.3 插入
        • 3.1.4 删除
        • 3.1.5 查找
          • 1)按序号查找结点
          • 2)按值查找结点
        • * 单链表完整代码
      • 3.2 双链表
        • 3.2.1 定义
        • 3.2.2 插入
        • 3.2.3 删除
        • 3.2.4 销毁
        • 3.2.5 查找
        • * 双链表完整代码
      • 3.3 循环链表
        • 3.3.1 循环单链表
        • 3.3.2 循环双链表
      • 3.4 静态链表
      • 3.5 顺序表和链表
        • 3.5.1 比较
        • 3.5.2 选取

二 线性表

1.定义与基本操作

  • 定义

    是有相同数据类型的n个数据元素的有限序列。

    其中n为表长,当n=0时线性表是一个空表。

    除最后一个数外,其他都有唯一后驱;从第一个数外,其他都有唯一前驱。

  • 什么时候要传入参数的引用“&”?

    对参数的修改结果需要从函数中带回来。

2.顺序表

2.1 定义

用顺序存储的方式实现线性表顺序存储。

把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中。

  • 如何知道一个元素大小?

    sizeof()

2.2 静态分配

#include<stdio.h>

#define ElemType int //定义数据类型


//静态定义顺序表
//一旦空间占满,会溢出
#define MaxSize 50 //定义线性表的最大长度
typedef struct {
	ElemType data[MaxSize]; //存顺序表的元素
	int length; //顺序表当前长度
}SqList; //顺序表的类型定义

//静态分配初始化
void InitList(SqList& L)
{
	L.length = 0;
}


//插入操作
//在第i位插入e,成功则返回true
//将第i个元素依次往后移动一个位置,腾出空位插入新元素e
bool ListInsert(SqList& L, int i, ElemType e)
{
	//判断i位置是否合理
	if (i<1 || i>L.length + 1)
	{
		return false;
	}
	//存储空间已满,不能插入
	if (L.length >= MaxSize)
	{
		return false;
	}
	//将第i个元素及之后的元素后移
	for (int j = L.length; j >= i; j--)
	{
		L.data[j] = L.data[j - 1];
	}
	L.data[i] = e;//位置i放入e
	L.length++;//长度加1
	return true;
}


//删除操作
//删除第i个元素,被删元素存在e
bool ListDelete(SqList& L, int i, ElemType& e)
{
	if (i<1 || i>L.length)
	{
		return false;
	}
	e = L.data[i-1];//将被删元素给i
	//将第i位后的元素前移
	for (int j = i; j < L.length; j++)
	{
		L.data[j - 1] = L.data[j];
	}
	L.length--;//长度减一
	return true;
}

//按值查找
//查找第一个等于e的元素,并返回其位序
int LocateElem(SqList L, ElemType e)
{
	int i;
	for (i = 0; i < L.length; i++)
	{
		if (L.data[i] == e)
		{
			return i + 1;//下标为i的元素,位序为i+1
		}
	}
	return 0;
}

void printList(SqList& L)
{
	for (int i = 0; i < L.length; i++)
	{
		printf_s("%d ", L.data[i]);
	}
	printf_s("\n");
}

int main()
{
	SqList L;
	InitList(L);
	//赋值
	for (int i = 0; i < 10; i++)
	{
		L.data[i] = i;
		L.length++;
	}
	printList(L);

	printf_s("插入:\n");
	int e = -1,pos=4;
	ListInsert(L, pos-1, e);
	printList(L);

	printf_s("删除:\n");
	int a;
	ListDelete(L, pos, a);
	printList(L);
	printf_s("%d\n", a);

	printf_s("查找:\n");
	pos=LocateElem(L, 5);
	printf_s("%d", pos);
}

3.链式表

3.1 单链表

3.1.1 定义

  • 注:

    元素离散的分布在存储空间中,所以单链表是非随机存取的存储结构。

    即不能直接找到表中某个特定的结点,需要从表头开始遍历,依次查找。

  • 定义的代码

    typedef struct LNode
    {
    	ElemType data;//每个节点存放一个数据元素
    	struct LNode* next;//指向下一个节点
    }LNode,*LinkList;
    
  • 头结点和头指针

  • 初始化

    分为带或不带头结点的初始化。

    一般用带头结点的链表,会使后续操作方便。

    • 不带头结点的初始化

      bool InitList(LinkList &L)
      {
          L=NULL;
          return ture;
      }
      
    • 带头结点的初始化

      bool InitList(LinkList& L)
      {
      	L = (LNode*)malloc(sizeof(LNode));//分配一个头节点
      	if (L == NULL)return false;//分配失败
      	L->next = NULL;
      	return true;
      }
      

3.1.2 建立

1)头插法建立单链表

在链表表头插入新结点。

  • 步骤

    1.将要插入结点的指针指向 原第一个结点;

    2.将头结点指针指向 要插入的结点。

//头插法建立单链表
LinkList List_HeadInsert(LinkList& L)
{
	LNode* s;
	int x;
	InitList(L);//初始化
	scanf_s("%d", &x);//输入结点值
	//插入表中
	while (x != 9999)//9999表示结束
	{
		//创建新结点
		s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = L->next;//新结点指针域指向原来的第一个结点
		//头结点指向新结点
		L->next = s;
		scanf_s("%d", &x);
	}
	return L;
}
2)尾插法建立单链表

在链表末尾插入新结点。

  • 步骤

    1.将尾结点的指针指向新结点;

    2.更新尾指针(新尾指针变成新结点)。

//尾插法建立链表
LinkList List_TailInsert(LinkList& L)
{
    LNode* s, * r = L;//r为表尾指针
    int x;
    scanf_s("%d", &x);//输入结点值
    //插入表中
    while (x != 9999)//9999表示结束
    {
        //创建新结点
        s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        //表尾指针指向新结点
        r->next = s;
        //更新表尾结点
        r = s;
        scanf_s("%d", &x);
    }
    r->next = NULL;//尾结点指针置空
    return L;
}

3.1.3 插入

  • 步骤

    1.查找插入位置的前驱结点;

    2.将插入结点的指针 指向 插入位置的后驱结点;

    3.将前驱结点的指针指向 插入结点。

//在表L中的第i个位置上插入指定元素e
bool ListInsert(LinkList& L, int i, ElemType e)
{
	if (i < 1)return false;
	LNode* p;//指针指向当前扫描到的节点
	int j = 0;//当前p指向的是第几个节点
	//查找第i个位置
	p = L;//L指向头节点,头节点是第0个节点
	while(p!=NULL&&j<i-1)
	{
		p = p->next;
		j++;
	}
	if (p == NULL)
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

3.1.4 删除

  • 步骤

bool ListDelete(LinkList& L, int i, ElemType& e)
{
	if (i < 1)return false;

	LNode* p;//指针p指向当前扫描到的结点
	int j = 0;//当前p指向的是第几个结点

	//循环找到第i-1个结点
	p = L;//指向头结点
	while (p != NULL && j < i - 1) {
		p = p->next;
		j++;
	}
	//i值不合法
	if (p == NULL||p->next==NULL)return false;

	LNode* q = p->next;//令q指向被删除结点
	e = q->data;//用e返回元素的值
	p->next = q->next;//将*q结点从链中断开
	free(q);//释放结点存储空间
	return true;
}

3.1.5 查找

1)按序号查找结点
//按序号查找结点
LNode* GetElem(LinkList L, int i)
{
	if (i < 1)return NULL;
	int j = 1;
	LNode* p = L->next;//第1个结点指针赋给p
	while (p != NULL && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}
2)按值查找结点
//按值查找
LNode* LocateElem(LinkList L, ElemType e)
{
	LNode* p = L->next;
	while (p != NULL && p->data != e)
	{
		p = p->next;
	}
	return p;
}

* 单链表完整代码

#include<stdio.h>
#include <malloc.h>
#include<time.h>//用于生成随机数
#include<stdlib.h>
#define ElemType int

//---------------定义---------------
typedef struct LNode
{
	ElemType data;//每个节点存放一个数据元素
	struct LNode* next;//指向下一个节点
}LNode,*LinkList;


初始化不带头节点的单链表
//bool InitList(LinkList& L) {
//	L = NULL;
//	return true;
//}
不带会麻烦

//初始化带头节点的单链表
bool InitList(LinkList& L)
{
	L = (LNode*)malloc(sizeof(LNode));//分配一个头节点
	if (L == NULL)return false;
	L->next = NULL;
	return true;
}


//---------------建立单链表---------------

//头插法建立单链表
LinkList List_HeadInsert(LinkList& L)
{
	int m;
	printf("\n1.手动输入\n2.自动生成\n");
	scanf_s("%d", &m);

	if (m == 1)
	{
		LNode* s;
		int x;
		scanf_s("%d", &x);//输入结点值
		//插入表中
		while (x != 9999)//9999表示结束
		{
			//创建新结点
			s = (LNode*)malloc(sizeof(LNode));
			s->data = x;
			s->next = L->next;//新结点指针域指向原来的第一个结点
			//头结点指向新结点
			L->next = s;
			scanf_s("%d", &x);
		}
		return L;
	}
	else if (m == 2)
	{
		LNode* s;
		int n = 10;//结点个数
		srand(time(0));//初始化随机数种子
		while (n--)
		{
			//创建新结点
			s = (LNode*)malloc(sizeof(LNode));
			s->data = rand() % 100 + 1;//随机产生100以内的数
			s->next = L->next;
			L->next = s;
		}
	}
	else
	{
		printf("请重新输入!\n");
		List_HeadInsert(L);
	}
	
}

//尾插法建立链表
LinkList List_TailInsert(LinkList& L)
{
	int m;
	printf("\n1.手动输入\n2.自动生成\n");
	scanf_s("%d", &m);

	if (m == 1)
	{
		LNode* s, * r = L;//r为表尾指针
		int x;
		scanf_s("%d", &x);//输入结点值
		//插入表中
		while (x != 9999)//9999表示结束
		{
			//创建新结点
			s = (LNode*)malloc(sizeof(LNode));
			s->data = x;
			//表尾指针指向新结点
			r->next = s;
			//更新表尾结点
			r = s;
			scanf_s("%d", &x);
		}
		r->next = NULL;//尾结点指针置空
		return L;
	}
	else if (m == 2)
	{
		LNode* s,*r=L;
		int n = 10;//结点个数
		srand(time(0));//初始化随机数种子
		while (n--)
		{
			//创建新结点
			s = (LNode*)malloc(sizeof(LNode));
			s->data = rand() % 100 + 1;//随机产生100以内的数
			r->next = s;
			r = s;
		}
		r->next = NULL;
		return L;
	}
	else
	{
		printf("请重新输入!\n");
		List_TailInsert(L);
	}
}


//---------------插入-----------------
//在表L中的第i个位置上插入指定元素e
bool ListInsert(LinkList& L, int i, ElemType e)
{
	if (i < 1)return false;
	LNode* p;//指针指向当前扫描到的节点
	int j = 0;//当前p指向的是第几个节点
	//查找第i个位置
	p = L;//L指向头节点,头节点是第0个节点
	while(p!=NULL&&j<i-1)
	{
		p = p->next;
		j++;
	}
	if (p == NULL)
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}


//---------------删除----------------
bool ListDelete(LinkList& L, int i, ElemType& e)
{
	if (i < 1)return false;

	LNode* p;//指针p指向当前扫描到的结点
	int j = 0;//当前p指向的是第几个结点

	//循环找到第i-1个结点
	p = L;//指向头结点
	while (p != NULL && j < i - 1) {
		p = p->next;
		j++;
	}
	//i值不合法
	if (p == NULL||p->next==NULL)return false;

	LNode* q = p->next;//令q指向被删除结点
	e = q->data;//用e返回元素的值
	p->next = q->next;//将*q结点从链中断开
	free(q);//释放结点存储空间
	return true;
}


//---------------查找----------------
//按序号查找结点
LNode* GetElem(LinkList L, int i)
{
	if (i < 1)return NULL;
	int j = 1;
	LNode* p = L->next;//第1个结点指针赋给p
	while (p != NULL && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}

//按值查找
LNode* LocateElem(LinkList L, ElemType e)
{
	LNode* p = L->next;
	while (p != NULL && p->data != e)
	{
		p = p->next;
	}
	return p;
}


void List_print(LinkList& L)
{
	LinkList t = L->next;
	while (t->next != NULL)
	{
		printf("%d,", t->data);
		t = t->next;
	}
	printf("%d\n", t->data);
}

void PutMenu()
{
	printf("\n----------菜单----------\n");
	printf("1.头插法创建单链表\n2 尾插法创建单链表\n");
	printf("3.插入元素\n");
	printf("4.删除元素\n");
	printf("5.按序号查找结点\n");
	printf("6.按值查找结点\n");

	printf("请选择想执行的操作:\n");
}


int main()
{
	LinkList L1;
	InitList(L1);

	//---------------菜单---------------
	PutMenu();
	int x;
	while (~scanf_s("%d", &x))
	{
		if (x == 1)
		{
			//头插法创建单链表
			List_HeadInsert(L1);
			List_print(L1);
		}
		else if (x == 2)
		{
			//尾插法创建单链表
			List_TailInsert(L1);
			List_print(L1);
		}
		else if (x == 3)
		{
			int pos, e;
			printf("请输入要插入的位置和元素:\n");
			scanf_s("%d %d", &pos, &e);
			ListInsert(L1, pos, e);
			List_print(L1);
		}
		else if (x == 4)
		{
			int pos, e;
			printf("请输入要删除的元素位置:\n");
			scanf_s("%d", &pos);
			ListDelete(L1, pos, e);
			printf("被删元素:%d\n",e);
			List_print(L1);
		}
		else if (x == 5)
		{
			LNode* p;
			int n;
			printf("请输入需要查找的序号:\n");
			scanf_s("%d", &n);
			p = GetElem(L1, n);
			printf("查找结点的值:%d", p->data);
		}
		else if (x == 6)
		{
			LNode* p;
			int n;
			printf("请输入需要查找的结点值:\n");
			scanf_s("%d", &n);
			p =LocateElem(L1, n);
			printf("success!");
		}
		PutMenu();
	}
	return 0;
}

3.2 双链表

3.2.1 定义

  • 作用

方便找上一个结点。

typedef struct DNode {
	ElemType data;
	struct DNode* prior, * next;//指向上一个和下一个的结点
}DNode,*DLinklist;
//DNode强调是结点,*DLinklist强调是链表

//初始化双链表
bool InitDLinkList(DLinklist& L) {
	L = (DNode*)malloc(sizeof(DNode));//分配一个头结点
	if (L == NULL)return false;
	L->prior = NULL;//头结点的prior永远指向null
	L->next = NULL;
	return true;
}

3.2.2 插入

  • 步骤

    1.插入节点的next指向后继节点;

    2.后继节点的prior指向插入节点;

    3.插入节点的prior指向前驱节点;

    4.前驱节点的next指向插入节点。

//p后插s
bool InsertNextDNode(DLinklist& L, int i, ElemType e) {
	if (i < 1)return false;

	DNode* p;//指针指向当前扫描到的节点
	int j = 0;//当前p指向的是第几个节点
	//查找第i个位置
	p = L;//L指向头节点,头节点是第0个节点
	while (p != NULL && j < i )
	{
		p = p->next;
		j++;
	}

	//非法参数
	if (p == NULL)return false;

	DNode* s = (DNode*)malloc(sizeof(DNode));
	s->data = e;
	s->next = p->next;//指向后继节点
	if (p->next != NULL)//如果插入位置不是链表末尾
	{
		p->next->prior = s;//后继节点指向插入节点
	}
	s->prior = p;//插入节点指向前驱节点
	p->next = s;//前驱节点指向插入节点
	return true;
}

//p前插s
//找到待插入位置的前驱节点,再进行后插
bool InsertPriorDNode(DLinklist& L, int i, ElemType e) {
	if (i < 1)return false;

	DNode* p;//指针指向当前扫描到的节点
	int j = 0;//当前p指向的是第几个节点
	//查找第i个位置
	p = L;//L指向头节点,头节点是第0个节点
	while (p != NULL && j < i - 1)
	{
		p = p->next;
		j++;
	}

	//非法参数
	if (p == NULL)return false;

	DNode* s = (DNode*)malloc(sizeof(DNode));
	s->data = e;
	s->next = p->next;//指向后继节点
	if (p->next != NULL)//如果插入位置不是链表末尾
	{
		p->next->prior = s;//后继节点指向插入节点
	}
	s->prior = p;//插入节点指向前驱节点
	p->next = s;//前驱节点指向插入节点
	return true;
}

3.2.3 删除

  • 步骤

    1.前驱节点的next指向后驱节点;

    2.后继节点的prior指向前驱节点;

    3.释放要删除的节点。

bool ListDelete(DLinklist& L, int i, ElemType& e)
{
	if (i < 1)return false;

	DNode* p;//指针p指向当前扫描到的结点
	int j = 0;//当前p指向的是第几个结点

	//循环找到第i-1个结点
	p = L;//指向头结点
	while (p != NULL && j < i - 1) {
		p = p->next;
		j++;
	}
	//i值不合法
	if (p == NULL || p->next == NULL)return false;

	DNode* q = p->next;//令q指向被删除结点
	e = q->data;//用e返回元素的值
	p->next = q->next;//将*q结点从链中断开
	if (q->next != NULL)//q不是最后一个
	{
		q->next->prior = p;
	}
	free(q);//释放结点存储空间
	return true;
}

3.2.4 销毁

相当于循环删除每个节点。

void DestoryList(DLinklist& L)
{
	//循环释放各节点
	DNode* p = L->next;
	while (p != NULL)
	{
		DNode* q = p->next;
		if (q != NULL)
		{
			p->next = q->next;
			if (q->next != NULL)
			{
				q->next->prior = p;
			}
			free(q);
		}
		p = p->next;
	}
	free(L);//释放头结点
	L = NULL;
}

3.2.5 查找

同单链表查找

时间复杂度:O(n)

* 双链表完整代码

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

#define ElemType int

//----------------定义-----------------
typedef struct DNode {
	ElemType data;
	struct DNode* prior, * next;//指向上一个和下一个的结点
}DNode,*DLinklist;
//DNode强调是结点,*DLinklist强调是链表
//*DNode等价于DLinklist

//初始化双链表
bool InitDLinkList(DLinklist& L) {
	L = (DNode*)malloc(sizeof(DNode));//分配一个头结点
	if (L == NULL)return false;
	L->prior = NULL;//头结点的prior永远指向null
	L->next = NULL;
	return true;
}


//---------------建立单链表---------------

//头插法建立单链表
DLinklist List_HeadInsert(DLinklist& L)
{
	int m;
	printf("\n1.手动输入\n2.自动生成\n");
	scanf_s("%d", &m);

	if (m == 1)
	{
		DNode* s;
		int x;
		scanf_s("%d", &x);//输入结点值
		//插入表中
		while (x != 9999)//9999表示结束
		{
			//创建新结点
			s = (DNode*)malloc(sizeof(DNode));
			s->data = x;
			s->next = L->next;//新结点指针域指向原来的第一个结点
			s->prior = L;
			//头结点指向新结点
			L->next = s;
			scanf_s("%d", &x);
		}
		return L;
	}
	else if (m == 2)
	{
		DNode* s;
		int n = 10;//结点个数
		srand(time(0));//初始化随机数种子
		while (n--)
		{
			//创建新结点
			s = (DNode*)malloc(sizeof(DNode));
			s->data = rand() % 100 + 1;//随机产生100以内的数
			s->next = L->next;
			L->next = s;
		}
	}
	else
	{
		printf("请重新输入!\n");
		List_HeadInsert(L);
	}

}

//尾插法建立链表
DLinklist List_TailInsert(DLinklist& L)
{
	int m;
	printf("\n1.手动输入\n2.自动生成\n");
	scanf_s("%d", &m);

	if (m == 1)
	{
		DNode* s, * r = L;//r为表尾指针
		int x;
		scanf_s("%d", &x);//输入结点值
		//插入表中
		while (x != 9999)//9999表示结束
		{
			//创建新结点
			s = (DNode*)malloc(sizeof(DNode));
			s->data = x;
			s->prior = r;
			//表尾指针指向新结点
			r->next = s;
			//更新表尾结点
			r = s;
			scanf_s("%d", &x);
		}
		r->next = NULL;//尾结点指针置空
		return L;
	}
	else if (m == 2)
	{
		DNode* s, * r = L;
		int n = 10;//结点个数
		srand(time(0));//初始化随机数种子
		while (n--)
		{
			//创建新结点
			s = (DNode*)malloc(sizeof(DNode));
			s->data = rand() % 100 + 1;//随机产生100以内的数
			r->next = s;
			r = s;
		}
		r->next = NULL;
		return L;
	}
	else
	{
		printf("请重新输入!\n");
		List_TailInsert(L);
	}
}


//-------------------插入操作-----------------------
//p后插s
bool InsertNextDNode(DLinklist& L, int i, ElemType e) {
	if (i < 1)return false;

	DNode* p;//指针指向当前扫描到的节点
	int j = 0;//当前p指向的是第几个节点
	//查找第i个位置
	p = L;//L指向头节点,头节点是第0个节点
	while (p != NULL && j < i )
	{
		p = p->next;
		j++;
	}

	//非法参数
	if (p == NULL)return false;

	DNode* s = (DNode*)malloc(sizeof(DNode));
	s->data = e;
	s->next = p->next;//指向后继节点
	if (p->next != NULL)//如果插入位置不是链表末尾
	{
		p->next->prior = s;//后继节点指向插入节点
	}
	s->prior = p;//插入节点指向前驱节点
	p->next = s;//前驱节点指向插入节点
	return true;
}

//p前插s
//找到待插入位置的前驱节点,再进行后插
bool InsertPriorDNode(DLinklist& L, int i, ElemType e) {
	if (i < 1)return false;

	DNode* p;//指针指向当前扫描到的节点
	int j = 0;//当前p指向的是第几个节点
	//查找第i个位置
	p = L;//L指向头节点,头节点是第0个节点
	while (p != NULL && j < i - 1)
	{
		p = p->next;
		j++;
	}

	//非法参数
	if (p == NULL)return false;

	DNode* s = (DNode*)malloc(sizeof(DNode));
	s->data = e;
	s->next = p->next;//指向后继节点
	if (p->next != NULL)//如果插入位置不是链表末尾
	{
		p->next->prior = s;//后继节点指向插入节点
	}
	s->prior = p;//插入节点指向前驱节点
	p->next = s;//前驱节点指向插入节点
	return true;
}


//-----------------删除------------------
bool ListDelete(DLinklist& L, int i, ElemType& e)
{
	if (i < 1)return false;

	DNode* p;//指针p指向当前扫描到的结点
	int j = 0;//当前p指向的是第几个结点

	//循环找到第i-1个结点
	p = L;//指向头结点
	while (p != NULL && j < i - 1) {
		p = p->next;
		j++;
	}
	//i值不合法
	if (p == NULL || p->next == NULL)return false;

	DNode* q = p->next;//令q指向被删除结点
	e = q->data;//用e返回元素的值
	p->next = q->next;//将*q结点从链中断开
	if (q->next != NULL)//q不是最后一个
	{
		q->next->prior = p;
	}
	free(q);//释放结点存储空间
	return true;
}


//-----------------销毁------------------
void DestoryList(DLinklist& L)
{
	//循环释放各节点
	DNode* p = L->next;
	while (p != NULL)
	{
		DNode* q = p->next;
		if (q != NULL)
		{
			p->next = q->next;
			if (q->next != NULL)
			{
				q->next->prior = p;
			}
			free(q);
		}
		p = p->next;
	}
	free(L);//释放头结点
	L = NULL;
}


//---------------查找----------------
//按序号查找结点
DNode* GetElem(DLinklist L, int i)
{
	if (i < 1)return NULL;
	int j = 1;
	DNode* p = L->next;//第1个结点指针赋给p
	while (p != NULL && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}

//按值查找
DNode* LocateElem(DLinklist L, ElemType e)
{
	DNode* p = L->next;
	while (p != NULL && p->data != e)
	{
		p = p->next;
	}
	if (p != NULL)
	{
		printf("Success!\n");
	}
	else
	{
		printf("没有这个值!\n");
	}
	return p;
}


void Menu()
{
	printf("\n----------菜单----------\n");
	printf("1.头插法创建\n2.尾插法创建\n");
	printf("3.后插\n4.前插\n");
	printf("5.删除节点\n6.销毁双链表\n");
	printf("7.按序号查找\n8.按值查找\n");
}

void Listprint(DLinklist& L)
{
	if (L == NULL)
	{
		printf("链表为空!\n");
		return;
	}
	DLinklist t = L->next;
	while (t->next != NULL)
	{
		printf("%d ", t->data);
		t = t->next;
	}
	printf("%d\n", t->data);
}


int main()
{
	DLinklist L;
	InitDLinkList(L);

	int x;
	Menu();
	while (~scanf_s("%d", &x)) {
		if (x == 1)
		{
			List_HeadInsert(L);
			Listprint(L);
		}
		else if (x == 2)
		{
			List_TailInsert(L);
			Listprint(L);
		}
		else if (x == 3)
		{
			int pos;
			ElemType data;
			printf("请输入要插入的位置和值:\n");
			scanf_s("%d %d", &pos,& data);
			InsertNextDNode(L, pos,data);
			Listprint(L);
		}
		else if (x == 4)
		{
			int pos;
			ElemType data;
			printf("请输入要插入的位置和值:\n");
			scanf_s("%d %d", &pos, &data);
			InsertPriorDNode(L, pos, data);
			Listprint(L);
		}
		else if (x == 5)
		{
			int pos;
			ElemType x;
			printf("请输入要删除的位置:\n");
			scanf_s("%d", &pos);
			ListDelete(L, pos, x);
			printf("已删除元素:%d\n", x);
			Listprint(L);
		}
		else if (x == 6)
		{
			DestoryList(L);
			printf("Success!\n");
			Listprint(L);
		}
		else if (x == 7)
		{
			DNode* p;
			int n;
			printf("请输入需要查找的序号:\n");
			scanf_s("%d", &n);
			p = GetElem(L, n);
			printf("查找结点的值:%d", p->data);
		}
		else if (x == 8)
		{
			DNode* p;
			int n;
			printf("请输入需要查找的结点值:\n");
			scanf_s("%d", &n);
			p = LocateElem(L, n);
		}

		Menu();
	}
	return 0;
}

3.3 循环链表

3.3.1 循环单链表

将最后一个节点指针指向头结点。

  • 好处

    1.可从表中任意一个节点开始遍历整个链表。

    2.可不设头指针而设尾指针,此时对表头表尾的操作时间复杂度都只需O(1)。

3.3.2 循环双链表

头节点的prior指针指向尾结点;

尾结点的next指针指向头结点。

  • 初始化循环双链表

    bool InitDLinkList(DLinklist &L){
    L=(DNode*)malloc(sizeof(DNode));
    if(L==NULL)return false;
    //头结点的prior指向头结点
    L->prior=L;
    //头结点的next指向头结点
    L->next=L;
    return true;
    }
    
  • 双链表判空

    bool Empty(DLinklist L)
    {
        if(L->next==L)
            return true;
        else
            return false;
    }
    

3.4 静态链表

  • 静态链表:分配一整片连续内存空间,各个节点集中安置。
  • 单链表:分散分布。

  • 定义

    相当于将单链表的next指针变成下一个元素下标。

    typedef struct{
        ElemType data;
        int next; //下一个元素的数组下标
    }SLinkList[MaxSize];
    

3.5 顺序表和链表

3.5.1 比较

  • 1.读取方式

    • 顺序表:可顺序存取,也可随机存取。
    • 链表:只能从表头顺序存取元素。
  • 2.逻辑结构和物理结构

    • 顺序表:逻辑上相邻,对应的物理存储位置也相邻。
    • 链表:逻辑上相邻,物理存储位置不一定相邻,对应的逻辑关系是通过指针链接来表示的。
  • 3.查找

    • 按值查找时,两者都相同。数据无序则 O ( n ) O(n) O(n),有序按折半查找为 O ( l o g 2 n ) O(log_2n) O(log2n)
    • 按序号查找时,
      • 顺序表:支持随机访问,所以为 O ( 1 ) O(1) O(1)
      • 链表:要遍历,所以为 O ( n ) O(n) O(n)
  • 4.查找和删除

    • 顺序表:平均要移动半个表长的元素。
    • 链表:修改相关节点指针域即可。
  • 5.空间分配

    • 顺序表:
      • 静态存储:不能扩充。
      • 动态存储:需移动大量元素,导致操作效率降低。如果内存中没有大块连续存储空间,会导致分配失败。
    • 链表:只在需要时申请分配,只要内存有空间就可以分配。

3.5.2 选取

  • 基于存储考虑

    难以估计线性表长度时,用链表。

  • 基于运算考虑

    • 按序号查找多时用顺序表。
    • 插入删除操作多时用链表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/693846.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【 云原生 kubernetes 】- 使用Filebeat采集k8s日志

文章目录 简介采集流程&#xff1a;镜像构建 Helm部署配置调整删除赘余部分更新Values挂载文件 主流程结果 ⚡️: 日志采集器Logstash其功能虽然强大&#xff0c;但是它依赖java、在数据量大的时候&#xff0c;Logstash进程会消耗过多的系统资源&#xff0c;这将严重影响业务系…

制作外网shell

安装配置环境 网址 https://www.ngrok.cc 支付两块钱之后&#xff0c;就可以免费使用服务器&#xff0c;进行隧道开通。远程端口就是服务器的端口&#xff0c;可以先随便写&#xff0c;等下会提示有哪一些可用端口 下载客户端&#xff0c;选择64位的版本&#xff08;跟kali一…

Lock接口介绍

1、 Synchronized (1) 多线程编程模板上 1) 第一步&#xff1a;创建资源类。 2) 第二步&#xff1a;创建操作方法。 3) 第三步&#xff1a;创建线程调用操作方法。 4&#xff09;原则&#xff1a;高内聚低耦合。 (2) 实现步骤 1&#xff09;创建资源类。 2&#xff09;资源类…

ubuntu下简单编译opencv

# 安装依赖&#xff0c;其中python-dev没有安装上没关系。 sudo apt install build-essential libgtk2.0-dev libavcodec-dev libavformat-dev libpng-dev libjpeg-dev libtiff5-dev libswscale-dev libjasper-dev libdc1394-22-dev libtiff-dev python-dev python-numpy libtb…

chatgpt赋能python:Python轨迹可视化:用数据讲故事

Python轨迹可视化&#xff1a;用数据讲故事 介绍 随着物联网、智能城市等领域的发展&#xff0c;越来越多的数据被收集下来并存储在数据库中。这些数据对于决策者来说是非常重要的&#xff0c;但是如何将这些数据进行展示和分析呢&#xff1f;这时候Python轨迹可视化就可以派…

java项目之后勤管理系统ssm源码+文档

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的后勤管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&a…

【docker创建容器的坑】WSL启动nvidia-docker镜像:报错libnvidia-ml.so.1- file exists- unknown

可能得错误一&#xff1a; 今天使用docker创建容器的时候总是出错&#xff0c;最后锁定问题在“--gpus all”这里&#xff1a; 不加--gpu all可以运行&#xff0c;加入了--gpus all就出错&#xff1a; docker: Error response from daemon: failed to create task for conta…

C语言进阶(反码和补码)

文章目录 前言一、反码二、补码三、符号位四、在程序中打印反码和补码总结 前言 本篇文章我们主要来讲解C语言中的反码和补码&#xff0c;这个可能是大家比较容易忽略的点&#xff0c;那么这篇文章就带大家来学习一下什么是反码和补码。 一、反码 反码是通过对整数的二进制表…

基于FPGA的多通道数据采集单元同步的实现

多通道数据采集设备在当前信息数字化的时代应用广泛&#xff0c;各种被测量的信息 如光线、温度、压力、湿度、位置等&#xff0c;都需要经过多通道信号采集系统的采样和 处理&#xff0c;才能被我们进一步分析利用[37]。在一些对采集速率要求较高的军事、航天、 航空、工业制造…

【JUC进阶】06. 轻量级锁

目录 1、前言 2、轻量级锁 2.1、什么是轻量级锁 2.2、工作原理 2.2.1、回顾Mark Word 2.2.2、工作流程 2.3、解锁 3、适用场景 4、注意事项 5、轻量级锁与偏向锁的对比 6、小结 1、前言 前面一节我们讲到了偏向锁。当偏向锁被撤销&#xff0c;或其他线程竞争的时候…

国内脑机接口最新研究 - 脑控机器人在非结构化场景执行避障任务

&#x1f482; 个人主页: 同学来啦&#x1f91f; 版权: 本文由【同学来啦】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助&#xff0c;欢迎关注、点赞、收藏和订阅专栏哦 文章目录 &#x1f31f; 一、能避障的脑控机器人导航&#x1f31f; 二、机器…

Moveit1 机器人配置包超详细解析

前言 Moveit的robot_moveit_config包里有很多文件&#xff0c;在配置时容易搞不清关系&#xff0c;导致配置过程艰难、漫长。 同时互联网上的文档几乎没有详细介绍这部分配置包的&#xff0c;大神各有各的配法&#xff0c;比较混乱。 因此笔者整理了配置包内各个重要的文件&…

Volvo EDI 项目 MySQL 方案开源介绍

近期为了帮助广大用户更好地使用 EDI 系统&#xff0c;我们根据以往的项目实施经验&#xff0c;将成熟的 EDI 项目进行开源。用户安装好知行之桥 EDI 系统之后&#xff0c;只需要下载我们整理好的示例代码&#xff0c;并放置在知行之桥指定的工作区中&#xff0c;即可开始使用。…

Win10安装SQLite Expert Pers Setup64

在win10系统下安装SQLite Expert&#xff0c;我的电脑是64位系统&#xff0c;所以选择SQLite Expert Pers Setup64 一、软件下载 1、官网地址 SQLite administration | SQLite Expert 点击“DOWNLOAD”&#xff0c;选择红框中的免费版本 2、百度网盘下载 链接&#xff1a;h…

SQL SERVER2008导入导入mdf和ldf文件

把需要添加的文件复制粘贴到这个路径下&#xff1a; 再回到这点击添加&#xff1a;

商品减库在Redis中的运用

一.商品减库中存在问题 1.传统的代码 1.1引入jar包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.…

chatgpt赋能python:Python转换DAT的方法详解

Python转换DAT的方法详解 在数据处理的应用场景中&#xff0c;我们经常要将DAT文件格式转换为其他数据格式&#xff0c;比如CSV或Excel等。此时&#xff0c;Python的数据处理能力就表现出其强大的优势。在本文中&#xff0c;我们将详细介绍使用Python转换DAT文件格式的方法。 …

chatgpt赋能python:在Python中如何实现超链接本地文件

在Python中如何实现超链接本地文件 介绍 超链接是在网页中实现页面跳转的一种技术&#xff0c;而本地文件指的是计算机本地存储的文件。在Python中&#xff0c;我们可以通过一些库来实现超链接本地文件。 具体来说&#xff0c;需要使用到Python中的web框架以及文件操作库&am…

重温数据结构与算法之KMP算法

文章目录 前言一、原理1.1 暴力法1.2 最长公共前后缀 二、代码实现2.1 next数组2.2 可视化next2.3 KMP 三、总结3.1 优点3.2 缺点 参考 前言 ​ KMP 算法是一种字符串匹配算法&#xff0c;它可以在一个主串中查找一个模式串的出现位置。在实际应用中&#xff0c;字符串匹配是一…

LangChain 任意命令执行(CVE-2023-34541)

漏洞简介 LangChain是一个用于开发由语言模型驱动的应用程序的框架。 在LangChain受影响版本中&#xff0c;由于load_prompt函数加载提示文件时未对加载内容进行安全过滤&#xff0c;攻击者可通过构造包含恶意命令的提示文件&#xff0c;诱导用户加载该文件&#xff0c;即可造…