数据结构与算法——线性顺序表原理及C语言实现

数据结构与算法——线性顺序表原理及C语言实现

  • 一、线性表顺序存储概念
  • 二、顺序表的基本属性程序设计
    • (1)线性表的创建 list_create
    • (2)清空顺序表 list_clear
    • (3)判断是否为空表 list_empty
    • (4)获取线性表长度 list_length
    • (5)显示线性表 list_show
    • (6)释放线性表内存空间 list_free
  • 三、顺序表的相关运算程序设计
    • (1)获取线性表元素 getVal
    • (2)定位线性表元素的下标 list_locate
    • (3)线性表插入元素(头插+尾插+任意位置)
      • 1. 头插法插入数据 list_push_front
      • 2. 尾插法插入数据 list_push_back
      • 3. 任意位置插入数据 list_insert
    • (4)根据索引删除元素 list_delete
    • (5)线性表的合并 list_merge
    • (5)线性表的去重 list_purge
  • 附件:测试程序及源程序

参考博文:程序内功篇二–线性顺序表

一、线性表顺序存储概念

  • 线性表示包含若干个数据元素的一个线性序列,记为:L=(a0,a1, … ai-1,ai,ai+1,…an-1),其中L为表名, ai为数据元素, n为表长
  • 若将线性表L=(a0,a1, …,an-1)中的各元素依次存储于计算机一片连续的存储空间。

线性表的特征:

  • 对非空表,a0 是表头,无前驱
  • an-1是表尾,无后继
  • 其他的每个元素 ai 有且仅有一个直接前驱 ai-1,和一个直接后继 ai+1

顺序存储结构的特点:

  • 逻辑上相邻的元素ai,ai+1,其存储位置也是相邻的
  • 对数据元素ai的存取为随机存取或按地址存取
  • 存储密度高

存储密度D=(数据结构中元素所占存储空间)/(整个数据结构所占空间)

顺序存储结构的表示:
在C语言中,可借助于结构体配合一维数组类型来描述线性表的顺序存储结构:

//变量与顺序表结构定义
#define N 100
typedef int data_t;

typedef struct{
	data_t data[N];	//表的存储空间
	int n;			//线性表中最后一个元素下标
}sqlist;

注意: n表示线性表最后一个元素的下标

二、顺序表的基本属性程序设计

(1)线性表的创建 list_create

 线性表的创建主要分为申请内存空间、初始化变量、返回顺序表指针三大步骤。

  • 功能:建立一个空的线性表
  • 参数:void
  • 返回值:线性表指针
    /*
    功能:建立一个空的线性表
    参数:void
    返回值:线性表指针
    */
    sqlist* list_create()
    {
    	//申请内存空间
    	sqlist* list = (sqlist *)malloc(sizeof(sqlist));
    	if(list == NULL){
    		printf("list_create: malloc error\n");
    		return NULL;
    	}
    	
    	//线性表成员变量赋值
    	list->n = -1;
    	
    	return list;
    }
    

(2)清空顺序表 list_clear

 清空顺序表主要对顺序表内部数据进行初始化,对结尾数据下标索引置-1:

  • 功能:清空线性表
  • 参数:线性表指针
  • 返回值:-1:参数错误   成功:0
    /*
    功能:清空线性表
    参数:线性表指针
    返回值:-1---参数错误   成功返回 0
    */
    int list_clear(sqlist* list)
    {
    	//入口 参数检查
    	if(list == NULL){
    		printf("list_clear: list is NULL");
    		return -1;
    	}
    	
    	//清空数组数据
    	int len = list_length(list);
    	memset(list->data, '\0', sizeof(data_t) * len);
    	list->n = -1;
    	
    	return 0;
    }
    

(3)判断是否为空表 list_empty

 判断顺序表是否为空表的关键是:判断结尾数据的索引是否为-1

  • 功能:判断线性表是否为空
  • 参数:线性表指针
  • 返回值:空:1   非空:0
    /*
    功能:判断线性表是否为空
    参数:线性表指针
    返回值:空:返回1  非空: 返回0
    */
    int list_empty(sqlist* list)
    {
    	if(list->n == -1)
    		return 1;
    	else
    		return 0;
    }
    

(4)获取线性表长度 list_length

 线性表的长度即是 最后一个元素索引+1

  • 功能:求线性表长度
  • 参数:线性表指针
    返回值:-1:参数错误  >0: 表长
    /*
    功能:求线性表长度
    参数:线性表指针
    返回值:-1---参数错误   >0 表长
    */
    int list_length(sqlist* list)
    {
    	//入口 参数检查
    	if(list == NULL){
    		printf("list_length: list is NULL");
    		return -1;
    	}
    	
    	//返回线性表长度
    	return list->n + 1;
    }
    

(5)显示线性表 list_show

 通过遍历线性表内连续空间实现对线性表的呈现

  • 功能:显示线性表
  • 参数:线性表指针
  • 返回值:void
    /*
    功能:显示线性表
    参数:线性表指针
    返回值:void
    */
    void list_show(sqlist* list)
    {
    	if(list->n == -1)
    		printf("list is NULL");
    	if(list != NULL)
    	{
    		for(int i = 0; i <= list->n; i++)
    			printf("%d ",list->data[i]);
    		printf("\n");
    	}
    	else
    		printf("list is NULL\n");
    }
    

(6)释放线性表内存空间 list_free

 通过遍历线性表实现内存的释放

  • 功能:释放线性表内存空间
  • 参数:线性表指针
  • 返回值:-1: 参数错误/为空  0:释放成功
    /*
    功能:释放线性表内存表空间
    参数:线性表指针
    返回值:-1: 参数错误/为空  0: 释放成
    */
    int list_free(sqlist* list)
    {
    	//入口参数检查
    	if(list == NULL)
    		return -1;
    	free(list);		//释放内存
    	list = NULL;	//避免成为野指针
    	return 0;	
    }
    

三、顺序表的相关运算程序设计

(1)获取线性表元素 getVal

 通过下标获取对应下标索引的值

  • 功能:根据下标获取某个元素
  • 参数:线性表指针
  • 返回值:-1: 参数错误/为空, 正常返回获取的元素
    /*
    功能:根据下标获取某个元素
    参数:线性表指针
    返回值:-1: 参数错误/为空  正常返回获取的元素
    */
    data_t list_getVal(sqlist* list, int index) //获取ai的值
    {
    	//入口 参数检查
    	if(list == NULL)
    		printf("list_getVal: list is NULL");
    	
    	return list->data[index];
    }
    

(2)定位线性表元素的下标 list_locate

 获取某个元素在线性表内的下标索引,同时可通过下标判断某个元素是否在线性表内(返回 -1 表示该元素不在表内)

  • 功能:获取某元素在线性表的下标
  • 参数:para1: 线性表指针, para2: 需要定位的元素值
  • 返回值:返回value在表中的下标, 不存在则返回-1
    /*
    功能:定位运算
    参数:线性表指针, 需要定位的元素值
    返回值:返回value在表中的下标, 不存在则返回-1
    */
    int list_locate(sqlist* list, data_t value)	//确定元素value在表中的下标
    {
    	if(list == NULL)
    		return -1;
    	
    	//比较value是否在 list内
    	for(int i = 0; i <= list->n; i++)
    	{
    		if(list->data[i] == value)
    			return i;
    	}
    	return -1;
    }
    

(3)线性表插入元素(头插+尾插+任意位置)

1. 头插法插入数据 list_push_front

 头插法插入数据在入口参数判断后:
 ①首先将所有数据往后移动1位
 ②接着将需要插入的数据存入线性表第一位
 ③记得线性表长度+1

  • 功能:头插法插入元素
  • 参数:para1:线性表指针  para2:待插入的元素
  • 返回值:-1: 参数错误  0: 插入成功
    /*
    功能:头插法插入元素
    参数:para1:线性表指针  para2:待插入的元素
    返回值:-1: 参数错误  0: 插入成功
    */
    int list_push_front(sqlist* list, data_t value)
    {
    	//入口参数检查
    	if(list == NULL || list->n >= N-1){
    		printf("push_front para error\n");
    		return -1;
    	}
    	
    	//右移 
    	list->n++;	//线性长+1
    	for(int i = list->n; i > 0; i--)
    		list->data[i] = list->data[i-1];
    	
    	//插入新数据
    	list->data[0] = value;
    	return 0;
    }
    
    头插法插入数据需要遍历移动整个线性表,因此插入数据的效率较低。

2. 尾插法插入数据 list_push_back

 尾插法只需表长+1,在线性表尾插入元素即可:

  • 功能:尾插法插入元素
  • 参数:para1:线性表指针  para2:待插入的元素
  • 返回值:-1: 参数错误  0: 插入成功
    /*
    功能:尾插法插入元素 
    参数:para1:线性表指针	para2:待插入的元素
    返回值:-1: 参数错误  0: 插入成功
    */
    int list_push_back(sqlist* list, data_t value)
    {
    	//入口参数检查
    	if(list == NULL || list->n >= N-1){
    		printf("push_back para error\n");
    		return -1;
    	}
    	
    	//线性表尾插入数据
    	list->n++;
    	list->data[list->n] = value;	
    	return 0;
    }
    
    尾插法插入数据直接在表尾插入即可,因此效率较高。

3. 任意位置插入数据 list_insert

 在任意位置插入元素需要判断线性表是否为空或已满,插入位置是否合法若插入位置大于表长,则在表尾插入,使线性表空间有序。
 若在合法位置插入数据,需要将插入位置后续的数据往后移1位,空出目标位置插入新的数据,同时线性表长度将+1

  • 功能:特定位置插入元素
  • 参数:para1:线性表指针 para2:待插入的元素
  • 返回值:-1:参数错误  0:插入成功
/*
功能:特定位置插入元素
参数:para1:线性表指针  para2:待插入的元素
返回值:-1:参数错误  0: 插入成功
*/
int list_insert(sqlist* list, int index, data_t value)
{
	//入口参数检查
	if(list == NULL){
		printf("list_insert para error\n");
		return -1;
	}
	//插入位置有误
	if(index < 0 || list->n >= N-1){
		printf("index index error\n");
		return -1;
	}
	
	//插入位置大,放表尾
	else if(index > list->n){
		list->n++;
		list->data[list->n] = value;	
		return 0;
	}
	else
	{
		list->n++;	//表长+1
		//右边数据后移 从后往前
		for(int i = list->n; i > index; i--)	
			list->data[i] = list->data[i-1];
		//插入数据
		list->data[index] = value;
		return 0;
	}
}

(4)根据索引删除元素 list_delete

 在删除元素之前先判断线性表是否为空,传入的删除下标位置是否合法,删除操作只需将目标位置后面的数据往前移1位即可

  • 功能:删除索引为index的元素
  • 参数:para1:线性表指针, para2:待删除的下标位置
  • 返回值:-1: 参数错误  0: 删除成功
/*
功能:删除索引为index的元素
参数:para1:线性表指针, para2:待删除的下标位置
返回值:-1: 参数错误  0: 删除成功
*/
int list_delete(sqlist* list, int index)
{
	//入口参数检查: 表空 / 删除索引非法
	if(list == NULL || list->n == -1 || index < 0 || index > list->n ){
		printf("list_delete para error\n");
		return -1;
	}
	
	//数据左移 覆盖即删除
	for(int i = index; i < list->n; i++){
		list->data[i] = list->data[i+1];
	}
	
	//线性表长度减1
	list->n--;	
	return 0;
}

(5)线性表的合并 list_merge

 线性表的合并即将list2中的元素合并至list1,如果list1中存在list2中的元素不并入,否则将list2的数据加入list1

  • 功能:线性表合并, list2并入list1
  • 参数:参数1:线性表1指针 参数2: 线性表2指针
  • 返回值:–1: 参数错误  0: 合并成功

  程序设计思路:通过遍历list2中的元素,判断list2中的每个元素是否在list1中出现过(list_locate)。若出现过则丢弃,若没有出现过,则尾插进list1(list_push_back)

/*
功能:线性表合并 list2并入list1
参数:参数1:线性表1指针 参数2: 线性表2指针
返回值:--1: 参数错误  0: 合并成功
*/
int list_merge(sqlist* list1, sqlist* list2)
{
	//入口参数错误
	if(list1 == NULL || list2 == NULL)
		return -1;
	
	for(int i = 0; i <= list2->n; i++)
	{
		int ret = list_locate(list1, list2->data[i]);
		//找到list2中有元素不在list1,尾插数据
		if(ret == -1){
			list_push_back(list1, list2->data[i]);	
		}		
	}
	return 0;
}

(5)线性表的去重 list_purge

 线性表的去重即去除表内的重复元素
程序设计思路: 依次遍历a1…an元素ai,判断ai是否在[a0,ai-1]内出现过,若出现过则删除ai,重新比较ai,若没有出现过,则进行下一轮判断。

  • 功能:删除线性表中重复元素
  • 参数:线性表指针指针
  • 返回值:-1: 参数错误   0: 删除成功
/*
功能:删除线性表中重复元素
参数:线性表指针
返回值:-1: 参数错误  	0: 删除成功
*/
//依次取i [1,n],判断ai是否在[0,ai-1]内
//若不在,i++,若在,删除ai,重新比ai
int list_purge(sqlist* list)
{
	//入口参数检查
	if(list == NULL){
		printf("list_purge: list error\n");
		return -1;
	}
	
	//只有1个数
	if(list->n == 0)
		return 0;
		
	int i = 1;
	//开始遍历 i [1,n]的元素
	while(i <= list->n)
	{
		int j = i-1;
		//开始遍历 [0,ai-1]元素
		while(j >= 0)
		{
			if(list->data[i] == list->data[j])
			{
				list_delete(list, i);
				break;
			}	
			else
				j--;	
		}
		
		//没找到 
		if(j < 0)
			i++;
	}
	return 0;
}

附件:测试程序及源程序

线性表插入与删除测试程序:

//线性表插入与删除测试
void test_inert_delete()
{
	sqlist* list = list_create();
	if(list == NULL){
		printf("list create error\n");
		return;
	}
	
	//头插 尾插 中间插 并显示
	list_push_back(list, 66);
	list_push_back(list, 100);
	list_push_back(list, 22);
	list_push_front(list,12);
	list_insert(list,3,76);
	list_show(list);
	
	//删除数据并显示
	list_delete(list,2);
	list_show(list);
	
	//释放表
	list_free(list);
}

线性表合并测试函数:

void test_merge()
{
	//创建线性表1
	sqlist* list1 = list_create();
	list_push_back(list1,10);
	list_push_back(list1,20);
	list_push_back(list1,30);
	
	//创建线性表2
	sqlist* list2 = list_create();
	list_push_back(list2,30);
	list_push_back(list2,50);
	
	//将表2合并至表1
	list_merge(list1,list2);
	list_show(list1);
	
	list_free(list1);
	list_free(list2);
}

测试删除重复元素:

void test_purge()
{
	//创建线性表1
	sqlist* list = list_create();
	list_push_back(list,10);
	list_push_back(list,10);
	list_push_back(list,10);
	list_push_back(list,10);
	list_push_back(list,10);
	list_push_back(list,10);
	
	list_show(list);
	list_purge(list);
	list_show(list);
	list_free(list);
}

sqlist.c:

#include "sqlist.h"

/********************函数实现*******************/
/*
功能:建立一个空的线性表
参数:void
返回值:线性表指针
*/
sqlist* list_create()
{
	//申请内存空间
	sqlist* list = (sqlist *)malloc(sizeof(sqlist));
	if(list == NULL){
		printf("list_create: malloc error\n");
		return NULL;
	}
	
	//线性表成员变量赋值
	list->n = -1;
	
	return list;
}

/*
功能:清空线性表
参数:线性表指针
返回值:-1---参数错误   成功返回 0
*/
int list_clear(sqlist* list)
{
	//入口 参数检查
	if(list == NULL){
		printf("list_clear: list is NULL");
		return -1;
	}
	
	//清空数组数据
	int len = list_length(list);
	memset(list->data, '\0', sizeof(data_t) * len);
	list->n = -1;
	
	return 0;
}

/*
功能:判断线性表是否为空
参数:线性表指针
返回值:空:返回1  非空: 返回0
*/
int list_empty(sqlist* list)
{
	if(list->n == -1)
		return 1;
	else
		return 0;
}

/*
功能:求线性表长度
参数:线性表指针
返回值:-1---参数错误   >0 表长
*/
int list_length(sqlist* list)
{
	//入口 参数检查
	if(list == NULL){
		printf("list_length: list is NULL");
		return -1;
	}
	
	//返回线性表长度
	return list->n + 1;
}



/*
功能:显示线性表
参数:线性表指针
返回值:void
*/
void list_show(sqlist* list)
{
	if(list->n == -1)
		printf("list is NULL");
	if(list != NULL)
	{
		for(int i = 0; i <= list->n; i++)
			printf("%d ",list->data[i]);
		printf("\n");
	}
	else
		printf("list is NULL\n");

}

/*
功能:释放线性表内存表空间
参数:线性表指针
返回值:-1: 参数错误/为空  0: 释放成
*/
int list_free(sqlist* list)
{
	if(list == NULL)
		return -1;
	free(list);
	list = NULL;
	return 0;	
}


/*
功能:根据下标获取某个元素
参数:线性表指针
返回值:-1: 参数错误/为空  正常返回获取的元素
*/
data_t list_getVal(sqlist* list, int index) //获取ai的值
{
	//入口 参数检查
	if(list == NULL)
		printf("list_getVal: list is NULL");
	
	return list->data[index];
}

/*
功能:定位运算
参数:线性表指针, 需要定位的元素值
返回值:返回value在表中的下标, 不存在则返回-1
*/
int list_locate(sqlist* list, data_t value)	//确定元素value在表中的下标
{
	if(list == NULL)
		return -1;
	
	//比较value是否在 list内
	for(int i = 0; i <= list->n; i++)
	{
		if(list->data[i] == value)
			return i;
	}
	return -1;
}

/*
功能:尾插法插入元素
参数:para1:线性表指针	para2:待插入的元素
返回值:-1: 参数错误  0: 插入成功
*/
int list_push_back(sqlist* list, data_t value)
{
	//入口参数检查
	if(list == NULL || list->n >= N-1){
		printf("push_back para error\n");
		return -1;
	}
	
	//线性表尾插入数据
	list->n++;
	list->data[list->n] = value;	
	return 0;
}

/*
功能:头插法插入元素
参数:para1:线性表指针	para2:待插入的元素
返回值:-1: 参数错误  0: 插入成功
*/
int list_push_front(sqlist* list, data_t value)
{
	//入口参数检查
	if(list == NULL || list->n >= N-1){
		printf("push_front para error\n");
		return -1;
	}
	
	//右移 
	list->n++;	//线性长+1
	for(int i = list->n; i > 0; i--)
		list->data[i] = list->data[i-1];
	
	//插入新数据
	list->data[0] = value;
	return 0;
}


/*
功能:特定位置插入元素
参数:para1:线性表指针	para2:待插入的元素
返回值:-1:参数错误  0: 插入成功
*/
int list_insert(sqlist* list, int index, data_t value)
{
	//入口参数检查
	if(list == NULL  || list->n >= N-1){
		printf("list_insert para error\n");
		return -1;
	}
	//线性表满了
	if(index < 0 || index > N){
		printf("index index error\n");
		return -1;
	}
	
	//插入位置大,放表尾
	else if(index > list->n){
		list->n++;
		list->data[list->n] = value;	
		return 0;
	}
	else
	{
		list->n++;	//表长+1
		//右边数据后移 从后往前
		for(int i = list->n; i > index; i--)	
			list->data[i] = list->data[i-1];
		//插入数据
		list->data[index] = value;
		return 0;
	}
}



/*
功能:删除索引为index的元素
参数:para1:线性表指针, para2:待删除的下标位置
返回值:-1: 参数错误  0: 删除成功
*/
int list_delete(sqlist* list, int index)
{
	//入口参数检查: 表空 / 删除索引非法
	if(list == NULL || list->n == -1 || index < 0 || index > list->n ){
		printf("list_delete para error\n");
		return -1;
	}
	
	//数据左移 覆盖即删除
	for(int i = index; i < list->n; i++){
		list->data[i] = list->data[i+1];
	}
	
	//线性表长度减1
	list->n--;	
	return 0;
}

/*
功能:线性表合并 list2并入list1
参数:参数1:线性表1指针 参数2: 线性表2指针
返回值:--1: 参数错误  0: 合并成功
*/
int list_merge(sqlist* list1, sqlist* list2)
{
	//入口参数错误
	if(list1 == NULL || list2 == NULL)
		return -1;
	
	for(int i = 0; i <= list2->n; i++)
	{
		int ret = list_locate(list1, list2->data[i]);
		//找到list2中有元素不在list1,尾插数据
		if(ret == -1){
			list_push_back(list1, list2->data[i]);	
		}		
	}
	return 0;
}

/*
功能:删除线性表中重复元素
参数:线性表指针
返回值:-1: 参数错误  	0: 删除成功
*/
//依次取i [1,n],判断ai是否在[0,ai-1]内
//若不在,i++,若在,删除ai,重新比ai
int list_purge(sqlist* list)
{
	//入口参数检查
	if(list == NULL){
		printf("list_purge: list error\n");
		return -1;
	}
	
	//只有1个数
	if(list->n == 0)
		return 0;
		
	int i = 1;
	//开始遍历 i [1,n]的元素
	while(i <= list->n)
	{
		int j = i-1;
		//开始遍历 [0,ai-1]元素
		while(j >= 0)
		{
			if(list->data[i] == list->data[j])
			{
				list_delete(list, i);
				break;
			}	
			else
				j--;	
		}
		
		//没找到 
		if(j < 0)
			i++;
	}
	return 0;
}

sqlist.h

#ifndef __SQLIST__H
#define __SQLIST__H

#include 
#include 
#include 

//变量与顺序表结构定义
#define N 100
typedef int data_t;

typedef struct{
	data_t data[N];	//表的存储空间
	int n;			//线性表中最后一个元素下标
}sqlist;

/*****************函数声明*******************/
/*************顺序表的基本属性***************/
sqlist* list_create(int n);		//建立一个空表
int list_clear(sqlist* list);	//清空表
int list_empty(sqlist* list);	//判断表是否为空
int list_length(sqlist* list);	//求线性表长度
void list_show(sqlist* list);	//显示线性表
int list_free(sqlist* list);	//释放线性表空间

/*************顺序表的相关运算***************/
data_t list_getVal(sqlist* list, int pos); 				//根据索引获取值
int list_locate(sqlist* list, data_t value);			//确定元素value的索引值
int list_push_back(sqlist* list, data_t value);			//尾插法
int list_push_front(sqlist* list, data_t value);		//头插法
int list_insert(sqlist* list, int pos, data_t value);	//在特定位置插入元素
int list_delete(sqlist* list, int index);				//删除一个元素
int list_merge(sqlist* list1, sqlist* list2);			//线性表合并 list2并入list1
int list_purge(sqlist* list1);							//删除线性表中重复元素

#endif

你可能感兴趣的:(数据结构与算法,c语言,数据结构)