初阶数据结构(C语言实现)——3顺序表和链表(1)

目录

  • 【本节目标】
  • [1. 线性表](#1. 线性表)
  • 2.顺序表
    • 2.1概念及结构
    • [2.2 接口实现](#2.2 接口实现)
      • [2.2.0 动态顺序表](#2.2.0 动态顺序表)
      • [2.2.1 顺序表初始化SLInit()](#2.2.1 顺序表初始化SLInit())
      • [2.2.2 销毁和打印](#2.2.2 销毁和打印)
      • [2.2.3 尾插SLPushBack()](#2.2.3 尾插SLPushBack())
      • [2.2.4 尾删SLPopBack()](#2.2.4 尾删SLPopBack())
      • [2.2.5 头插](#2.2.5 头插)
      • [2.2.6 头删](#2.2.6 头删)
      • [2.2.7 插入](#2.2.7 插入)
      • [2.2.8 删除](#2.2.8 删除)
      • [2.2.9 查找函数](#2.2.9 查找函数)
    • [2.3 源代码](#2.3 源代码)
      • [2.3.1 SeqList20250226.h](#2.3.1 SeqList20250226.h)
      • [2.3.2 SeqList20250226.c](#2.3.2 SeqList20250226.c)
      • [2.3.3 FileName20250226.c](#2.3.3 FileName20250226.c)

【本节目标】

1.线性表

2.顺序表

3.链表

4.顺序表和链表的区别和联系

1. 线性表

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

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

2.顺序表

2.1概念及结构

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

  1. 静态顺序表:使用定长数组存储元素。
  2. 动态顺序表:使用动态开辟的数组存储。

2.2 接口实现

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

2.2.0 动态顺序表

c 复制代码
//seqList.h
#pragma once
#include<stdio.h>
typedef int SLDataType;
#define INIT_CAPACITY 4 //默认初始化大小
//动态顺序表------按需申请
typedef struct SeqList
{
	SLDataType* a;
	int size; //有效数据个数
	int capacity;//空间容量
}SL;

2.2.1 顺序表初始化SLInit()

版本1

c 复制代码
//seqList.c
#include"SeqList20250226.h"
//顺序表初始化1
void seqInit(SL s)
{
	s.a = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);
	if (s.a == NULL)
	{
		perror("seqInit");
		return;
	}

	s.size = 0;
	s.capacity = INIT_CAPACITY;
}
c 复制代码
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList20250226.h"

SL s;
void TestSeqList1(SL s)
{
    seqInit(s);
}
int main()
{
    
    TestSeqList1(s);
    return 0;
}

初始化结果:单步调试
版本2(后续都使用版本2)

c 复制代码
//顺序表初始化2
void SLInit(SL* ps)
{
	ps->a = ((SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY));
	if (ps->a == NULL)
	{
		perror("SLInit");
		return;
	}
	ps->size = 0;
	ps->capacity = INIT_CAPACITY;
}

2.2.2 销毁和打印

c 复制代码
//seqList.h
//销毁
void SLDestroy(SL* ps);
//打印
void SLPrint(SL* ps);
c 复制代码
//seqList.c
//销毁
void SLDestroy(SL* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}
//打印
void SLPrint(SL* ps)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d", ps->a[i]);
	}
	printf("\n");
}

2.2.3 尾插SLPushBack()

seqList.h

c 复制代码
#include<stdlib.h>
//尾插
void SLPushBack(SL* ps, SLDataType x);

seqList.c

c 复制代码
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
//扩容 这里扩容了2倍,是自己定的,是一个比较合理的值,具体需要开辟多大是要和自己的实际问题结合
	if (ps->size == ps->capacity)
	{
		SLDataType* tem = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ((ps->capacity) * 2));
		if (tem == NULL)
		{
			perror("SLPushBack::realloc");
			return;
		}
		ps->a = tem;
		ps->capacity *= 2;
	}
	//ps->a[ps->size] = x;
	//ps->size++;  这两行可以合并为下面的1行
	ps->a[ps->size++] = x;
}

尾插验证

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList20250226.h"
void TestSeqList1()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);
	SLPushBack(&s, 6);
	SLPushBack(&s, 7);
	SLPushBack(&s, 8);
	SLPushBack(&s, 9);
	SLPrint(&s);
}

int main()
{
    
    TestSeqList1();
    return 0;
}

2.2.4 尾删SLPopBack()

c 复制代码
//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	//判断是否为空
	if (ps->size == 0)
	{
		return 0;
	}
	//不为空就直接size-1
	ps->size--;
}

验证插入4个元素,删掉1个元素

2.2.5 头插

头插思路

扩容函数封装

c 复制代码
void SLCheckCapacity(SL* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)//判断目前是大小是否和容量大小一样,一样的话,就需要扩容
	{
		SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * (ps->capacity) * 2); //扩容原来容量的两倍
		if (tmp == NULL)
		{
			perror("SLCheckCapacity::realloc fail!"); 
			return;
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
}

代码实现

c 复制代码
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);

	//这里要考虑扩容的事情,所以我们需要封装一个扩容函数,
	SLCheckCapacity(ps);
	int end =ps->size-1;
	while (end>=0)
	{
		ps->a[end+1] = ps->a[end ];//从后往前依次向后挪
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}

头插验证

2.2.6 头删

思路

c 复制代码
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);//size小于0 直接断言报错。
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;

}

头删验证

2.2.7 插入

思路

c 复制代码
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (pos < end)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

验证

2.2.8 删除

思路

c 复制代码
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int begin = pos + 1;
	while (begin <ps->size)
	{
		ps->a[begin-1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

验证

2.2.9 查找函数

思路:比较简单,直接遍历即可

c 复制代码
int SLFind(SL* ps, SLDataType x)//直接遍历即可
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			printf("找到了");
			return i;
		}
	}
	return -1;
}

2.3 源代码

2.3.1 SeqList20250226.h

c 复制代码
#pragma once

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

//#define N 100
//typedef int SLDataType;
// 静态顺序表 -- 开少了不够用 开多了浪费
//struct SeqList
//{
//	SLDataType a[N];
//	int sise;
//};


typedef int SLDataType;
#define INIT_CAPACITY 4 //默认初始化大小
//动态顺序表------按需申请
typedef struct SeqList
{
	SLDataType* a;
	int size; //有效数据个数
	int capacity;//空间容量
}SL;

//基础的增删查改函数

//初始化
void seqInit(SL* ps);
void SLInit(SL* ps);
//销毁
void SLDestroy(SL* ps);
//打印
void SLPrint(SL* ps);

//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
头插
void SLPushFront(SL* ps, SLDataType x);
头删
void SLPopFront(SL* ps);

// 顺序表在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x);

// 顺序表删除pos位置的值
void SLErase(SL* ps, int pos);

//查找表中有没有某个值
int SLFind(SL* ps, SLDataType x);

2.3.2 SeqList20250226.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList20250226.h"



//顺序表初始化1
void seqInit(SL s)
{
	s.a = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);
	if (s.a == NULL)
	{
		perror("seqInit");
		return;
	}

	s.size = 0;
	s.capacity = INIT_CAPACITY;
}
//顺序表初始化2
void SLInit(SL* ps)
{
	ps->a = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);
	if (ps->a == NULL)
	{
		perror("SLInit");
		return;
	}
	ps->size = 0;
	ps->capacity = INIT_CAPACITY;
}


//销毁
void SLDestroy(SL* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}
//打印
void SLPrint(SL* ps)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}


//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	//判断是否要扩容
	if (ps->size == ps->capacity)
	{
		SLDataType* tem = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * (ps->capacity) * 2);
		if (tem == NULL)
		{
			perror("SLPushBack::realloc  fail");
			return;
		}
		ps->a = tem;
		ps->capacity *= 2;
	}
	//ps->a[ps->size] = x;
	//ps->size++;  //这两行可以合并为下面的1行
	ps->a[ps->size++] = x;
}

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	//判断是否为空
	if (ps->size == 0)
	{
		return 0;
	}
	//不为空就直接size-1
	ps->size--;
}

void SLCheckCapacity(SL* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * (ps->capacity) * 2);
		if (tmp == NULL)
		{
			perror("SLCheckCapacity::realloc fail!"); 
			return;
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
}

void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);

	//这里要考虑扩容的事情,所以我们需要封装一个扩容函数,
	SLCheckCapacity(ps);
	int end =ps->size-1;
	while (end>=0)
	{
		ps->a[end+1] = ps->a[end ];//从后往前依次向后挪
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}


void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);//size小于0 直接断言报错。
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;

}


void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (pos <= end)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int begin = pos + 1;
	while (begin <ps->size)
	{
		ps->a[begin-1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}


int SLFind(SL* ps, SLDataType x)//直接遍历即可
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			printf("找到了");
			return i;
		}
	}
	return -1;
}

2.3.3 FileName20250226.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList20250226.h"


void TestSeqList1()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 0);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
//	SLPopBack(&s);
// 
//	SLPushFront(&s,5);
//  SLPopFront(&s);

//	SLInsert(&s, 2, 20);
	SLErase(&s, 2);
	SLPrint(&s);
	SLDestroy(&s);
}


void manu()
{
	printf("**************************\n");
	printf("****1:头插      2.尾插****\n");
	printf("****3.头删      4.尾删****\n");
	printf("****5.插入      -1.退出****\n");
	printf("**************************\n");
}

int main()
{
	SL s;
	SLInit(&s);
	int input = 0;
	while (input != -1)
	{
		manu();
		scanf("%d", &input);
		printf("请输入您的选择:\n");
		if (input == 1)
		{
			printf("请输入您想要插入的数据,并以-1结尾\n");
			int x = 0;
			while (x != -1)
			{
				scanf("%d", &x);
				SLPushBack(&s, x);
			}
		}
		else if (input == 7)
		{
			SLPrint(&s);
		}
	}
 
	
	//TestSeqList1();
    return 0;
}
相关推荐
yonuyeung2 小时前
代码随想录算法【Day57】
数据结构·算法
graceyun2 小时前
初阶数据结构(C语言实现)——3顺序表和链表(2)
c语言·数据结构·链表
charlie1145141912 小时前
深入讨论C语言的可能抽象:部分对设计模式的思考
c语言·学习·设计模式·软件工程
非衣居士3 小时前
游戏编程模式(28种编程模式)
数据结构·游戏开发
Fms_Sa3 小时前
将两个有序链表合并成一个有序链表
数据结构·链表
Jared_devin3 小时前
数据结构——哈夫曼树
数据结构
猫猫的小茶馆3 小时前
基于嵌入式linux的数据库:SQLite
linux·服务器·c语言·数据库·单片机·ubuntu·sqlite
juechen3334 小时前
如何更改vim命令创建代码文件时的默认模板
linux·c语言·编辑器·vim·vimplus
七七七七074 小时前
浅谈C++/C命名冲突
c语言·c++
查理零世4 小时前
【数据结构】 最大最小堆实现优先队列 python
数据结构·python