基础数据结构和算法二:顺序表

0. 线性结构

数据结构中最常用最简单的结构是线性结构。
线性结构,又称线性表。逻辑结构上数据元素之间存在一个对一个的相邻关系。线性结构是n个数据元素的有序(次序)集合,它有下列几个特征:

1.集合中必存在唯一的一个"第一个元素";
2.集合中必存在唯一的一个"最后的元素";
3.除最后元素之外,其它数据元素均有唯一的"后继";
4.除第一元素之外,其它数据元素均有唯一的"前驱"。


1. 顺序表是什么?

顺序表是用一组地址连续的存储单元依次存储线性表中的各个元素,使线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中。

数组的缺点:大小(元素个数)不能改变,不能适用元素个数变化的情况。
数组可以看作无法改变大小的顺序表。

2. 顺序表怎么用?

顺序表通过一个结构体和结构体对应的接口使用。

3. 顺序表怎么实现?

3.1 定义结构

typedef int SeqType;
typedef struct
{
	SeqType* data;
	int size;
}List;

定义一个存储单元类型SeqType是为了使顺序表适和更多数据类型,使用的时候修改SeqType类型即可。

3.2 定义操作

对比学习 序号
初始化–销毁 1–2
头部插入–尾部插入 3
插入元素–删除元素 4–5
获取元素–获取元素个数 6-- 7
  1. 创建顺序表
// 创建
List list_create(){
	List l = {NULL,0};
	return l;
}
  1. 初始化顺序表
    为什么要初始化?因为局部变量默认初始化为随机值。
    怎么初始化?把结构体变量成员赋值为合法的初始值。
 // 初始化
bool list_init(List* seq){
	assert(NULL != seq);
	seq->data = NULL;
	seq->size = 0;

}
  1. 销毁顺序表
    // 销毁
void list_destroy(List* seq){
	assert(NULL != seq);
	free(seq->data);
	seq->data = NULL;
	seq->size = 0;
}
  1. 添加元素(头部和尾部的差别:需不需要拷贝元素的过程,即移动)。
    方案一:尾部添加
    //3.1.1 复杂版(malloc+realloc)
    (1)情况一(头部添加):开辟一个空间,填充值;
    (2)情况二(其他添加):在seq->data基础上添加一个空间,填充值第size的值;
    总体:size++;
void list_append(List* seq,SeqType val){
	assert(NULL != seq);
	if(NULL == seq->data){
		seq->data = malloc(sizeof(SeqType));
		seq->data[0] = val;
	}else{
		seq->data = realloc(seq->data,seq->size+1);
		seq->data[seq->size] = val;
	}
	seq->size++;
}

//3.1.2 进阶版(realloc)
//size先自增;
//(1)多开辟一块空间;
//(2)往回1个进行填充值;

void list_append(List* seq,SeqType val){
	assert(NULL != seq);
	seq->size++;
	seq->data = realloc(seq->data,seq->size*sizeof(SeqType));
	seq->data[seq->size-1] = val;
}

方案二:头部添加
3.2.1 循环的方法
//(1)size先自增;
//(2)多开辟一块空间;
//(3)往回1个进行填充值;首先确定第1个值为val,其他值往后进行倒推;

 void list_prepend(List* seq,SeqType val){
	assert(NULL != seq);
	seq->size++;
	seq->data = realloc(seq->data,seq->size*sizeof(SeqType));
	for(int i=seq->size-1;i>0;--i){
		seq->data[i] = seq->data[i-1];
	}
	seq->data[0] = val;
}

3.2.2 内存拷贝的方法
//(1)size先自增;
//(2)多开辟一块空间;
//(3)往回1个进行填充值;首先确定第1个值为val,其他值往后进行倒推;
(memcpy():注意:①指针地址的传递,传递的位置②大小,多开辟的空间需要减去)。

扫描二维码关注公众号,回复: 9188054 查看本文章
void list_prepend(List* seq,SeqType val){
	assert(NULL != seq);
	seq->size++;
	seq->data = realloc(seq->data,seq->size*sizeof(SeqType));
	memcpy(seq->data+1,seq->data,(seq->size-1)*sizeof(SeqType));
	seq->data[0] = val;
}
  1. 插入元素
    注意
    (1)注意拷贝的起始位置
    (2)拷贝的 大小;
    (3)下标的位置;
// 插入数据
void list_insert(List* seq,int index,SeqType val){
	assert(NULL != seq);
	assert(index >=0 && seq->size >= index);
	seq->size++;
	seq->data = realloc(seq->data,seq->size*sizeof(SeqType));
	memcpy(seq->data+index+1,seq->data+index,(seq->size-1-index)*sizeof(SeqType));
	seq->data[index] = val;
}
  1. 删除元素
    (1)注意拷贝的起始位置
    (2)拷贝的 大小;
    (3)index下标的位置(数组的要求);
// 删除数据
void list_delete(List* seq,int index){
	assert(NULL != seq);
	assert(index >=0 && seq->size > index);
	memcpy(seq->data+index,seq->data+index+1,(seq->size-index)*sizeof(SeqType));
	seq->size--;
}
  1. 获取元素
    注意:根据下标(index)返回需要的值;
// 获取数据
SeqType list_get(List* seq,int index){
	assert(NULL != seq);
	assert(index >=0 && seq->size > index);
	return seq->data[index];
}
  1. 获取元素个数

注意:根据规模(seq->size)返回需要的值;

// 获取元素个数
int list_size(List* seq){
	assert(NULL != seq);
	return seq->size;
}
  1. 遍历
    注意:
    (1)结构体指针的用法;typedef void (traval_t)(SeqType val);
    (2)其他函数的调用:(*func)(seq->data+i);
// 遍历
typedef void (*traval_t)(SeqType* val);
void list_traval(List* seq,traval_t func){
	assert(NULL != seq);
	assert(NULL != func);
	for(int i=0;i<seq->size;++i){
		(*func)(seq->data+i);
	}
}
  1. 功能性函数1:打印
// 打印
void list_print(List* seq){
	assert(NULL != seq);
	printf("&data = %p\n",seq->data);
	printf("size=%d\n",seq->size);
	for(int i=0;i<seq->size;++i){
		printf("%d ",seq->data[i]);
	}
	printf("\n");
}
  1. 功能性函数2:比较年龄
static int list_comp_element(const void* a,const void* b){
	return ((SeqType*)a)->age - ((SeqType*)b)->age;
}
  1. 功能性函数3:比较函数
void list_sort(List* seq){
	qsort(seq->data,seq->size,sizeof(SeqType),list_comp_element);
}

3.3 如何把实际的结构体和顺序表结构相结合

(1)SeqType 数据类型需要改变

 // 顺序表结构体
typedef struct{
	char name[20];
	bool sex;
	int age;
} Student;

typedef Student SeqType;

typedef struct {
	SeqType* data;// 动态数组
	int size; 	  // 元素个数
} List;

(2)整体代码
①main.c

#include <stdio.h>
#include "SeqList.h"

void PrintElement(SeqType* val){
	printf("name:%s\tsex:%s\tage:%d\n",val->name,val->sex?"男":"女",val->age);
}
void Grow(SeqType* val){
	val->age++;
}
int main(){
	List l;
	list_init(&l);

	SeqType a = {"张三",true,30};
	list_append(&l,a);
    SeqType b = {"李四",false,20};
	list_append(&l,b);
    SeqType c = {"王五",false,25};
	list_prepend(&l,c);


	list_traval(&l,PrintElement);

	list_delete(&l,1);
	list_traval(&l,Grow);
	list_traval(&l,PrintElement);
}

②SeqList.h

#ifndef _SEQLIST_H
#define _SEQLIST_H
#include <stdbool.h>

// 顺序表结构体
typedef struct{
	char name[20];
	bool sex;
	int age;
} Student;

typedef Student SeqType;

typedef struct {
	SeqType* data;// 动态数组
	int size; 	  // 元素个数
} List;

// 初始化
bool list_init(List* seq);
// 销毁
void list_destroy(List* seq);

// 基本操作
// 添加数据
void list_append(List* seq,SeqType val);
void list_prepend(List* seq,SeqType val);
// 插入数据
void list_insert(List* seq,int index,SeqType val);
// 删除数据
void list_delete(List* seq,int index);
// 获取数据
SeqType list_get(List* seq,int index);
// 获取元素个数
int list_size(List* seq);

// 打印
void list_print(List* seq);
// 遍历
typedef void (*traval_t)(SeqType* val);
void list_traval(List* seq,traval_t func);

// int list_comp_element(const SeqType* a,const SeqType* b)

void list_sort(List* seq);
#endif // _SEQLIST_H

③SeqList.c

#include "SeqList.h"
#include <assert.h>
#include <stdio.h> // NULL
#include <stdlib.h> // malloc() relloc()
#include <string.h> // memcpy()
// 初始化
bool list_init(List* seq){
	assert(NULL != seq);
	seq->data = NULL;
	seq->size = 0;

}
// 销毁
void list_destroy(List* seq){
	assert(NULL != seq);
	free(seq->data);
	seq->data = NULL;
	seq->size = 0;
}

// 基本操作
// 添加数据
void list_append(List* seq,SeqType val){
	assert(NULL != seq);
	/*
	if(NULL == seq->data){
		seq->data = malloc(sizeof(SeqType));
		seq->data[0] = val;
	}else{
		seq->data = realloc(seq->data,seq->size+1);
		seq->data[seq->size] = val;
	}
	seq->size++;
	*/
	seq->size++;
	seq->data = realloc(seq->data,seq->size*sizeof(SeqType));
	seq->data[seq->size-1] = val;
}
void list_prepend(List* seq,SeqType val){
	assert(NULL != seq);
	seq->size++;
	seq->data = realloc(seq->data,seq->size*sizeof(SeqType));
	/*
	for(int i=seq->size-1;i>0;--i){
		seq->data[i] = seq->data[i-1];
	}*/
	memcpy(seq->data+1,seq->data,(seq->size-1)*sizeof(SeqType));
	seq->data[0] = val;
}
// 插入数据
void list_insert(List* seq,int index,SeqType val){
	assert(NULL != seq);
	assert(index >=0 && seq->size >= index);
	seq->size++;
	seq->data = realloc(seq->data,seq->size*sizeof(SeqType));
	memcpy(seq->data+index+1,seq->data+index,(seq->size-1-index)*sizeof(SeqType));
	seq->data[index] = val;
}
// 删除数据
void list_delete(List* seq,int index){
	assert(NULL != seq);
	assert(index >=0 && seq->size > index);
	memcpy(seq->data+index,seq->data+index+1,(seq->size-index)*sizeof(SeqType));
	seq->size--;
}
// 获取数据
SeqType list_get(List* seq,int index){
	assert(NULL != seq);
	assert(index >=0 && seq->size > index);
	return seq->data[index];
}
// 获取元素个数
int list_size(List* seq){
	assert(NULL != seq);
	return seq->size;
}
// 遍历
typedef void (*traval_t)(SeqType* val);
void list_traval(List* seq,traval_t func){
	assert(NULL != seq);
	assert(NULL != func);
	for(int i=0;i<seq->size;++i){
		(*func)(seq->data+i);
	}
}

// 打印
void list_print(List* seq){
	assert(NULL != seq);
	printf("&data = %p\n",seq->data);
	printf("size=%d\n",seq->size);
	for(int i=0;i<seq->size;++i){
		printf("%d ",seq->data[i]);
	}
	printf("\n");
}

static int list_comp_element(const void* a,const void* b){
	return ((SeqType*)a)->age - ((SeqType*)b)->age;
}

void list_sort(List* seq){
	qsort(seq->data,seq->size,sizeof(SeqType),list_comp_element);
}

4. 优化

  1. 容量
    每次增加一个元素,都要重复释放申请内存。可以预先申请一部分备用。
    每次预先申请多少?
    //头部尾部添加元素
    seq->data = realloc(seq->data,(seq->size+CAPACITY)*sizeof(SeqType));
#define CAPACITY 10 // 容量
//(1)尾部添加元素
void list_append(List* seq,SeqType val){
	assert(NULL != seq);
	seq->size++;
	if((seq->size-1)%CAPACITY == 0){
	    seq->data = realloc(seq->data,(seq->size+CAPACITY)*sizeof(SeqType));
	}
	seq->data[seq->size-1] = val;
}2)头部添加元素
void list_prepend(List* seq,SeqType val){
	assert(NULL != seq);
	seq->size++;
	if((seq->size-1)%CAPACITY == 0){
		seq->data = realloc(seq->data,(seq->size+CAPACITY)*sizeof(SeqType));
	}
	/*
	for(int i=seq->size-1;i>0;--i){
		seq->data[i] = seq->data[i-1];
	}*/
	memcpy(seq->data+1,seq->data,(seq->size-1)*sizeof(SeqType));
	seq->data[0] = val;
}
  1. 随机访问元素
    获取元素sqlist_get(),只能获取到顺序表中的元素的副本,如果需要改变顺序表中的元素,可以提供如下函数。
 SeqType* list_at(List* seq,int index){
	assert(NULL != seq);
	assert(index >=0 && seq->size > index);
	return seq->data+index;

}
  1. 遍历 (上面)
    提供一个对顺序表的整体操作的接口。

改进后的代码:
①main.c

#include <stdio.h>
#include "SeqList.h"

void PrintElement(SeqType* val){
	printf("name:%s\tsex:%s\tage:%d\n",val->name,val->sex?"男":"女",val->age);
}
void Grow(SeqType* val){
	val->age++;
}
int main(){
	/*
	List l;
	list_init(&l);
	*/
	List l = list_create();

	SeqType a = {"张三",true,30};
	list_append(&l,a);
    SeqType b = {"李四",false,20};
	list_append(&l,b);
    SeqType c = {"王五",false,25};
	list_prepend(&l,c);


	list_traval(&l,PrintElement);

	list_delete(&l,1);
	list_traval(&l,Grow);
	printf("-------\n");
	SeqType* d = list_at(&l,0);
	d->age = 100;
	list_traval(&l,PrintElement);

	list_destroy(&l);
}

②SeqList.c

#include "SeqList.h"
#include <assert.h>
#include <stdio.h> // NULL
#include <stdlib.h> // malloc() relloc()
#include <string.h> // memcpy()

#define CAPACITY 10 // 容量

// 创建
List list_create(){
	List l = {NULL,0};
	return l;
}
// 初始化
bool list_init(List* seq){
	assert(NULL != seq);
	seq->data = NULL;
	seq->size = 0;

}
// 销毁
void list_destroy(List* seq){
	assert(NULL != seq);
	free(seq->data);
	seq->data = NULL;
	seq->size = 0;
}

// 基本操作
// 添加数据
void list_append(List* seq,SeqType val){
	assert(NULL != seq);
	/*
	if(NULL == seq->data){
		seq->data = malloc(sizeof(SeqType));
		seq->data[0] = val;
	}else{
		seq->data = realloc(seq->data,seq->size+1);
		seq->data[seq->size] = val;
	}
	seq->size++;
	*/
	seq->size++;
	if((seq->size-1)%CAPACITY == 0){
	    seq->data = realloc(seq->data,(seq->size+CAPACITY)*sizeof(SeqType));
	}
	seq->data[seq->size-1] = val;
}
void list_prepend(List* seq,SeqType val){
	assert(NULL != seq);
	seq->size++;
	if((seq->size-1)%CAPACITY == 0){
		seq->data = realloc(seq->data,(seq->size+CAPACITY)*sizeof(SeqType));
	}
	/*
	for(int i=seq->size-1;i>0;--i){
		seq->data[i] = seq->data[i-1];
	}*/
	memcpy(seq->data+1,seq->data,(seq->size-1)*sizeof(SeqType));
	seq->data[0] = val;
}
// 插入数据
void list_insert(List* seq,int index,SeqType val){
	assert(NULL != seq);
	assert(index >=0 && seq->size >= index);
	seq->size++;
	if((seq->size-1)%CAPACITY == 0){
		seq->data = realloc(seq->data,(seq->size+CAPACITY)*sizeof(SeqType));
	}
	memcpy(seq->data+index+1,seq->data+index,(seq->size-1-index)*sizeof(SeqType));
	seq->data[index] = val;
}
// 删除数据
void list_delete(List* seq,int index){
	assert(NULL != seq);
	assert(index >=0 && seq->size > index);

	memcpy(seq->data+index,seq->data+index+1,(seq->size-index)*sizeof(SeqType));
	seq->size--;
}
// 获取数据
SeqType list_get(List* seq,int index){
	assert(NULL != seq);
	assert(index >=0 && seq->size > index);
	return seq->data[index];
}
SeqType* list_at(List* seq,int index){
	assert(NULL != seq);
	assert(index >=0 && seq->size > index);
	return seq->data+index;

}
// 获取元素个数
int list_size(List* seq){
	assert(NULL != seq);
	return seq->size;
}
// 遍历
typedef void (*traval_t)(SeqType* val);
void list_traval(List* seq,traval_t func){
	assert(NULL != seq);
	assert(NULL != func);
	for(int i=0;i<seq->size;++i){
		(*func)(seq->data+i);
	}
}

// 打印
void list_print(List* seq){
	assert(NULL != seq);
	printf("&data = %p\n",seq->data);
	printf("size=%d\n",seq->size);
	for(int i=0;i<seq->size;++i){
		printf("%d ",seq->data[i]);
	}
	printf("\n");
}

static int list_comp_element(const void* a,const void* b){
	return ((SeqType*)a)->age - ((SeqType*)b)->age;
}

void list_sort(List* seq){
	qsort(seq->data,seq->size,sizeof(SeqType),list_comp_element);
}

③SeqList.h

#ifndef _SEQLIST_H
#define _SEQLIST_H
#include <stdbool.h>

// 顺序表结构体
typedef struct{
	char name[20];
	bool sex;
	int age;
} Student;

typedef Student SeqType;

typedef struct {
	SeqType* data;// 动态数组
	int size; 	  // 元素个数
} List;
// 创建
List list_create();
// 初始化
bool list_init(List* seq);
// 销毁
void list_destroy(List* seq);

// 基本操作
// 添加数据
void list_append(List* seq,SeqType val);
void list_prepend(List* seq,SeqType val);
// 插入数据
void list_insert(List* seq,int index,SeqType val);
// 删除数据
void list_delete(List* seq,int index);
// 获取数据
SeqType list_get(List* seq,int index);
SeqType* list_at(List* seq,int index);
// 获取元素个数
int list_size(List* seq);

// 打印
void list_print(List* seq);
// 遍历
typedef void (*traval_t)(SeqType* val);
void list_traval(List* seq,traval_t func);

// int list_comp_element(const SeqType* a,const SeqType* b)

void list_sort(List* seq);

// 清空
void list_clear(List* seq);
// 判空
bool list_empty(List* seq);
// 查找
int list_find(List* seq,SeqType val);
SeqType* list_search(List* seq,SeqType val);

#endif // _SEQLIST_H

6. 练习

  1. 实现一个清空接口
  2. 实现一个判空接口
  3. 实现一个查找指定元素的接口,返回元素下标
  4. 实现一个查找指定元素的接口,返回元素地址
  5. 实现比较两个元素的接口
  6. 实现交换两个元素的接口
发布了33 篇原创文章 · 获赞 8 · 访问量 2266

猜你喜欢

转载自blog.csdn.net/Pierce110110/article/details/104036362