初阶数据结构——顺序表和链表(单链表)

news2024/12/23 22:38:44

目录

    • 1.线性表
    • 2.顺序表
      • SeqList.h
      • SeqList.c
      • Test.c
      • 数组习题
        • 移除元素
        • 删除有序数组中的重复项
        • 合并两个有序数组
      • 顺序表的问题及思考
    • 3.链表
      • SList.h
      • SList.c
      • Test.c

1.线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储

顺序表:
在这里插入图片描述
链表:
在这里插入图片描述

2.顺序表

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

顺序表一般可以分为:
1、静态顺序表:使用定长数组存储元素。
2、动态顺序表:使用动态开辟的数组存储。

主要介绍动态

在这里插入图片描述

SeqList.h

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


// 静态的顺序表
// 给小了不够用,给多了浪费
//#define N 10000
//typedef int SLDatatype;
//struct SeqList
//{
//	SLDatatype a[N];
//	int size;
//};

// 动态顺序表
//typedef double SLDatatype;
typedef int SLDatatype;
typedef struct SeqList
{
	SLDatatype* a;
	int size;       // 存储的有效数据的个数
	int capacity;   // 容量
}SL;

void SLInit(SL* psl);
void SLDestroy(SL* psl);

void SLPrint(SL* psl);

//STL命名风格
void SLPushBack(SL* psl, SLDatatype x);
void SLPushFront(SL* psl, SLDatatype x);
void SLPopBack(SL* psl);
void SLPopFront(SL* psl);

void SLInsert(SL* psl, int pos, SLDatatype x);
void SLErase(SL* psl, int pos);

// 找到返回下标,没有找到返回-1
int SLFind(SL* psl, SLDatatype x);
void SLModify(SL* psl, int pos, SLDatatype x);

SeqList.c

void SLInit(SL* psl)
{
	assert(psl);
	psl->a = (SLDatatype*)malloc(sizeof(SLDatatype)*4);
	if (psl->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	psl->capacity = 4;
	psl->size = 0;
}

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

void SLPrint(SL* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}

void SLCheckCapacity(SL* psl)
{
	assert(psl);

	if (psl->size == psl->capacity)
	{
		SLDatatype* tmp = (SLDatatype*)realloc(psl->a, sizeof(SLDatatype) * psl->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		psl->a = tmp;
		psl->capacity *= 2;
	}
}

void SLPushBack(SL* psl, SLDatatype x)
{
	assert(psl);

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

	//SLCheckCapacity(psl);

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

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

void SLPushFront(SL* psl, SLDatatype x)
{
	assert(psl);
	//SLCheckCapacity(psl);

	 挪动数据
	//int end = psl->size - 1;
	//while (end >= 0)
	//{
	//	psl->a[end + 1] = psl->a[end];
	//	--end;
	//}

	//psl->a[0] = x;
	//psl->size++;
	SLInsert(psl, 0, x);
}

void SLPopBack(SL* psl)
{
	assert(psl);
	// 暴力检查
	//assert(psl->size > 0);

	 温柔的检查
	if (psl->size == 0)
		return;

	psl->a[psl->size - 1] = 0;
	//psl->size--;

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

void SLPopFront(SL* psl)
{
	assert(psl);
	// 暴力检查
	//assert(psl->size > 0);

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

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

	//psl->size--;

	SLErase(psl, 0);
}

void SLInsert(SL* psl, int pos, SLDatatype x)
{
	assert(psl);
	//assert(0 <= pos <= psl->size);
	assert(0 <= pos &&  pos<= psl->size);

	SLCheckCapacity(psl);

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

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

void SLErase(SL* psl, int pos)
{
	assert(psl);
	assert(0 <= pos && pos < psl->size);
	//assert(psl->size > 0);

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

	psl->size--;
}

int SLFind(SL* psl, SLDatatype x)
{
	assert(psl);

	for (int i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}

void SLModify(SL* psl, int pos, SLDatatype x)
{
	assert(psl);

	assert(0 <= pos && pos < psl->size);

	psl->a[pos] = x;
}

Test.c

注意:如果scanf()函数使用的是%d转换说明符,那么当程序读取的第一个字符为非数字字符比如‘A’时,scanf()将会读取失败,‘A’会待在缓冲区,所以得先清空缓冲区才能让scanf继续读取

void menu()
{
	printf("***************************************\n");
	printf("1、尾插数据  2、尾删数据\n");
	printf("3、头插数据  4、头删数据\n");
	printf("5、打印数据  -1、退出\n");
	printf("***************************************\n");
}

int main()
{
	int option = 0;
	SL s;
	SLInit(&s);
	while (option != -1)
	{
		menu();
		printf("请输入你的操作:>");
		scanf("%d", &option);
		if (option == 1)
		{
			/*printf("请输入要尾插的数据,以-1结束:");
			int x = 0;
			scanf("%d", &x);
			while (x != -1)
			{
				SLPushBack(&s, x);
				scanf("%d", &x);
			}*/

			int n = 0;
			printf("请输入要尾插的数据个数,再依次输入要插入的数据:");
			scanf("%d", &n);

			int x = 0;
			while (n > 0)
			{
				scanf("%d", &x);
				SLPushBack(&s, x);
				n--;
			}
		}
		else if (option == 5)
		{
			SLPrint(&s);
		}
		else if (option == -1)
		{
			break;
		}
		else
		{
			printf("无此选项,请重新输入\n");
		}
	}

	SLDestroy(&s);

	return 0;
}

数组习题

移除元素

在这里插入图片描述

暴力求解:
在这里插入图片描述

空间换时间:
在这里插入图片描述

双指针:
在这里插入图片描述

int removeElement(int* nums, int numsSize, int val){
    int pos=0,prev=0;
    for(int i=0;i<numsSize;i++)
    {
        if(nums[pos]==val)
        {
            pos++;
        }
        else
        {
            nums[prev++]=nums[pos++];
        }
    }
    return prev;
}

删除有序数组中的重复项

在这里插入图片描述

int removeDuplicates(int* nums, int numsSize){
    int pos=0,prev=0;
    while(pos<numsSize)
    {
        if(nums[pos]==nums[prev])
        {
            pos++;
        }
        else
        {
            prev++;
            nums[prev]=nums[pos];
        }
    }
    return prev+1;
}

合并两个有序数组

在这里插入图片描述
两个数组都从后往前比较可以节省空间

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    //归并,从后往前比较,放到nums1中
    int len1=m-1;
    int len2=n-1;
    int k=m+n-1;
    while(len1>=0 && len2>=0)
    {
        if(nums1[len1]>nums2[len2])
        {
            nums1[k--]=nums1[len1--];
        }
        else
        {
            nums1[k--]=nums2[len2--];
        }
    }
    while(len2>=0)
    {
        nums1[k--]=nums2[len2--];
    }
}

顺序表的问题及思考

顺序表优势:下标的随机访问
问题:

  1. 中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到
    200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

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

3.链表

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

SList.h

#pragma once

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

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

void SLTPrint(SLTNode* phead);
void SLPushFront(SLTNode** pphead, SLTDataType x);
void SLPushBack(SLTNode** pphead, SLTDataType x);

void SLPopFront(SLTNode** pphead);
void SLPopBack(SLTNode** pphead);

// 单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SLInsertAfter(SLTNode* pos, SLTDataType x);

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);

// 删除pos位置后面的值
void SLEraseAfter(SLTNode* pos);

SList.c

void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}

	printf("NULL\n");
}

SLTNode* BuyLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);  // 链表为空,pphead也不为空,因为他是头指针plist的地址
	//assert(*pphead); // 不能断言,链表为空,也需要能插入

	SLTNode* newnode = BuyLTNode(x);

	newnode->next = *pphead;
	*pphead = newnode;
}

//void SLPushBack(SLTNode* phead, SLTDataType x)
//{
//	SLTNode* tail = phead;
//	while (tail != NULL)
//	{
//		tail = tail->next;
//	}
//
//	SLTNode* newnode = BuyLTNode(x);
//	tail = newnode;
//}

void SLPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
	//assert(*pphead); // 链表为空,可以尾插

	SLTNode* newnode = BuyLTNode(x);

	// 1、空链表
	// 2、非空链表
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

void SLPopFront(SLTNode** pphead)
{
	assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
	assert(*pphead); // 链表为空,不能头删。(当然你还可以用温柔的检查)

	SLTNode* del = *pphead;
	*pphead = (*pphead)->next;
	free(del);

	// 一个节点
	// 多个节点
	//if ((*pphead)->next == NULL)
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	//else
	//{
	//	SLTNode* del = *pphead;
	//	//*pphead = del->next;
	//	*pphead = (*pphead)->next;
	//	free(del);
	//}
}

void SLPopBack(SLTNode** pphead)
{
	assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
	assert(*pphead); // 链表为空,不能头删。(当然你还可以用温柔的检查)

	// 没有节点(空链表)

	// 暴力检查
	//assert(*pphead);

	// 温柔的检查
	/*if (*pphead == NULL)
	{
		return;
	}*/

	// 一个节点
	// 多个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//SLTNode* prev = NULL;
		//SLTNode* tail = *pphead;
		 找尾
		//while (tail->next)
		//{
		//	prev = tail;
		//	tail = tail->next;
		//}

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

		SLTNode* tail = *pphead;
		// 找尾
		while (tail->next->next)
		{
			tail = tail->next;
		}

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

SLTNode* STFind(SLTNode* phead, SLTDataType x)
{
	//assert(phead);

	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	//assert(*pphead);

	if (*pphead == pos)
	{
		SLPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

// 在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = BuyLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
	}
}

void SLEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

Test.c

void TestSList4()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLTPrint(plist);

	SLTNode* pos = STFind(plist, 3);
	if (pos)
	{
		SLInsert(&plist, pos, 30);
	}
	SLTPrint(plist);

	pos = STFind(plist, 2);
	if (pos)
	{
		SLInsertAfter(pos, 20);
	}
	SLTPrint(plist);

	pos = STFind(plist, 2);
	if (pos)
	{
		SLErase(&plist, pos);
	}
	SLTPrint(plist);
}

int main()
{
	TestSList4();

	return 0;
}

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

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

相关文章

BI技巧丨计算组折线图

PowerBI中&#xff0c;通常我们会使用折线图来展示数据的趋势情况。但是当数据类别过多的时候&#xff0c;需求也在进一步深入&#xff0c;往往还需要我们将用户关注的重要节点标注出来&#xff0c;例如&#xff1a;最大值和最小值。 很早之前&#xff0c;白茶曾经写过一篇关于…

HTML学习笔记一

目录 HTML学习笔记 一、HTML标签 1、HTML语法规范 1.1标签的语法概述 1.2标签关系 2、HTML基本结构标签 2.1第一个HTML 2.2基本结构标签总结 3、开发工具 4、HTML常用标签 4.1标签的语义 4.2标题标签 4.3段落和换行标签 4.4文本格式化标签 4.5div和span标签 4.…

Linux云服务器的使用,以及运行Python程序、相关Linux指令

目录 1、使用Linux云服务器的软件 2、Linux系统运行Python程序 3、Linux系统查看包、虚拟环境、安装包等 以下几个深度学习服务器都不错&#xff1a;智星云、AutoDL、恒源云 1、使用Linux云服务器的软件 MobaXterm_Personal 推荐MobaXterm_Personal mobaxterm是一款方便网站…

基于Flask的留言板的设计与实现

这是《Flask Web开发实战:入门、进阶与原理解析》这本书中的一个小项目&#xff0c;我在学习后根据书中的教程实现了留言板的功能&#xff0c;并结合我的思路将代码做了一些调整。 下面这是实现后的展示图片 文章目录 设计思路项目代码exts.pymodels.pyforms.pyerrors.pycomma…

DMDTS:DM迁移到SQL脚本

DMDTS:DM迁移到SQL脚本 环境介绍1 注册工程2 新建迁移3 迁移工具介绍4 选择迁移方式5 配置数据源6 配置SQL脚本文件7 配置源端获取对象方式和迁移策略8 选择指定对象复制9 选择迁移对象9.1 迁移对象的配置 - - 转换 设置表的映射关系 10 审阅迁移任务11 完成迁移 环境介绍 DM管…

一文了解使用Moonbeam原生跨链的潜力项目

跨链互连合约是Moonbeam独特的原生跨链解决方案&#xff0c;不仅帮助开发团队在Moonbeam网络即可解锁不同公链的特色功能&#xff0c;而且各类去中心化应用的终端使用者能因此获得更便捷安全的跨链体验。 Moonbeam的原生跨链解决方案包含Polkadot生态、不同异构链和Moonbeam生…

hitcontraining_uaf

1&#xff0c;三连 基本信息&#xff1a;x86-32-el,堆题思路&#xff1b; 保护&#xff1a;Partial RELRO。 堆题多看一个Libc&#xff1a; 2,IDA分析 main功能&#xff1a; add_note()功能&#xff1a; malloc了两次&#xff1a; 8字节填充&#xff08;利用点之一&#xf…

Markdown快速入门教程

Markdown 的目标是实现「易读易写」&#xff0c;并强调它的「可读性」&#xff0c;因此Markdown 的语法全由标点符号所组成&#xff0c;并经过严谨慎选&#xff0c;是为了让它们看起来就像所要表达的意思&#xff1b;以下是Markdown 大部分的语法。 常用语法- 文字样式 文字字…

Allegro过孔盖油和过孔开窗设置(部分过孔开窗)

Allegro设置一部分过孔盖油&#xff0c;另一部分过孔开窗。 过孔开窗&#xff1a;过孔部分去除阻焊&#xff0c;便于调试和散热&#xff1b; 过孔盖油&#xff1a;过孔盖上阻焊油墨&#xff0c;防止过孔连锡短路。 总结 使用pad designer设计两种via pad&#xff0c;一种不开…

分布式事务的几种解决方案

一.基础概念 1. 什么是事务 事务可以看做是一次大的活动&#xff0c;它由不同的小活动组成&#xff0c;这些活动 要么全部成功&#xff0c; 要么全部失败 2.本地事务 在计算机系统中&#xff0c;更多的是通过 关系型数据库来控制事务&#xff0c;这是利用数据库 本身的事务特性…

基于Qt的教务管理系统的设计与实现

获取代码&#xff1a; (1) 下载链接: https://download.csdn.net/download/kese7952/87741551 (2) 添加博主微信获取,备注来源: mryang511688 项目描述 技术&#xff1a;C、QT等 摘要&#xff1a; 随着学校规模的不断扩大&#xff0c;学生的流动变迁导致了学校在管理学生信息…

二叉堆(Binary Heap)

二叉堆&#xff08;Binary Heap&#xff09; 二叉堆概述优先级队列 二叉堆&#xff08;Binary Heap&#xff09;其实比较简单&#xff0c;但却非常有用&#xff0c;常见的应用二叉堆排序和优先级队列。本文将介绍二叉堆的基本性质、基本操作和二叉堆在优先级队列上的应用。 二…

abc 283E 经典dp

题意&#xff1a;https://www.luogu.com.cn/problem/AT_abc283_e 思路&#xff1a;非常经典的dp&#xff0c;设为前i行第i行是否反转和第i1行是否反转。 /*keep on going and never give up*/ #include<cstdio> #include<iostream> #include<queue> #inclu…

【Mysql】基础篇:DDL (data definition language) 总结

博主简介&#xff1a;努力学习的大一在校计算机专业学生&#xff0c;热爱学习和创作。目前在学习和分享&#xff1a;数据结构、Go&#xff0c;Java等相关知识。博主主页&#xff1a; 是瑶瑶子啦所属专栏: Mysql从入门到精通近期目标&#xff1a;写好专栏的每一篇文章 目录 一、…

pytest自动化框架之allure测试报告的用例描述设置

allure测试报告的用例描述相关方法&#xff1b;如下图 allure标记用例级别severity 在做自动化测试的过程中&#xff0c;测试用例越来越多的时候&#xff0c;如果执行一轮测试发现了几个测试不通过&#xff0c;我们也希望能快速统计出缺陷的等级。 pytest结合allure框架可以对…

Linux基础指令大讲解

文章目录 1.linux的历史2.如何安装linux3.如何创建和删除用户4.目录&#xff0c;文件&#xff0c;文件夹区别5.pwd6.ls6.1基础语法6.2常用选项 7.alias 和 which的配套使用7.1基础格式7.2解释7.3与其关联的一些命令 8.cd8.1相对路径和绝对路径8.2运行可执行程序的方法8.3cd的基…

Java学习(韩顺平670-675)

目录 在我看来&#xff0c;我们在Java需要不断封包的原因就是为了使用不同类库中自己所需的方法&#xff0c;我们才要进行封装的&#xff0c;你怎么看&#xff1f; 你好&#xff0c;什么叫做关闭外层流 关于字符流要求 服务端/客户端 TCP网络通信编程//●//应用案例4 TCPFil…

dbForge Edge 4in1 Enterprise Edition Crack

单一解决方案中的四个数据库 IDE dbForge Edge 赋予自己开发和管理 SQL Server、MySQL、Oracle 和 PostgreSQL 数据库的广泛能力 概述 dbForge Edge&#xff1a;您的终极多数据库解决方案 让我们来看看。您需要处理多个数据库管理系统。同时&#xff0c;您希望能够灵活有效地…

好用的项目管理软件的具体功能有哪些

随着企业规模不断的扩大&#xff0c;项目管理往往会面临更多的挑战与难题&#xff0c;最常见的会出现以下几个问题&#xff1a;资源消耗失控&#xff0c;而项目部门和相关部门之间沟通越来越困难&#xff1b;团队凝聚力下降、项目进度难以把控&#xff0c;项目成本几乎失控&…

书接上回,接着研究当今世上缓存第一人

书接上回&#xff0c;接着研究当今世上缓存第一人&#xff0c;记得留下的关注&#xff0c;收藏起来以后观看。 五 三大特殊数据类型 1 geospatial&#xff08;地理位置&#xff09; 1.geospatial将指定的地理空间位置&#xff08;纬度、经度、名称&#xff09;添加到指定的ke…