数据结构——顺序表

镇楼图

Pixiv:きのこ姫



顺序表

首先需要明确采用顺序表的一些细节问题

静态实现还是动态实现?

即内存空间大小是否固定,在使用顺序表时会采用动态实现,但有时如果只是临时用用,为了方便则会用静态实现,即直接定义数组

是否带头节点?

顺序表一般不会带头节点,但下标和位序不等,可以置空\(a[0]\)元素来让下标、位序相等,同时可以在顺序查找时可以少比较一次

是否循环,即是否将表尾表头相连?

一般情况也不会做这种处理,但实现也不算难

选择什么参数?

必选参数是为了构建数据结构,可选参数是为了拓展功能

框架

typedef struct{
	int value;
}element,*Element;
typedef struct{
	Element data;	//表头地址
    int len;		//可容纳长度
    int cnt;		//当前长度
}clist;

基本操作

(1)创建空顺序表

void clist_create(clist &L,int n){
    //作用:创建可容纳长度为length的空顺序表
    L.data = (Element)calloc(sizeof(element),n);
    if(!L.data){
        printf("错误:内存分配错误");
        exit(-1);
    }
    L.len = n;
    L.cnt = 0;
    return;
}

(2)输入

这里简单用scanf输入数据

void clist_input(clist &L,int n){
	//作用:向后输入n个数据
    if(L.cnt + n > L.len){
        printf("错误:溢出");
        return;
    }
    for(int i = 0;i < n;i++){
        scanf("%d ",&L.data[i+L.cnt]);
    }
    L.cnt += n;
}

(3)输出

void clist_output(clist &L){
    //作用:打印顺序表
    for(int i = 0;i < L.cnt;i++){
        printf("%d ",L.data[i]);
    }
}

(4)判断顺序表是否为空/满

bool clist_ifempty(clist &L){
    //作用:判断顺序表是否为空
	return (!L.cnt) ? true : false ;
}

bool clist_iffull(clist &L){
	//作用:判断顺序表是否已满
    return (L.cnt == L.len) ? true : false;
}

(5)表尾追加元素

void clist_append(clist &L,element e){
	if(clist_iffull(L)){
		printf("错误:溢出");
        return;
    }
    L.data[L.cnt++] = e;
}

(6)其他操作

element clist_get(clist &L,int i){
    //作用:获取位序为i的元素
	if(i <= 0 || i > L.cnt || clist_ifempty(L)){
		printf("错误:错误位置或空顺序表");
        element e = {0};
        return e;
    }
    return L.data[i-1];
}
element clist_gethead(clist &L){
    //作用:获取表头
	if(clist_ifempty(L)){
		printf("错误:空顺序表");
        element e = {0};
        return e;
    }
    return L.data[0];
}
element clist_getlast(clist &L){
	//作用:获取表尾
    if(clist_ifempty(L)){
		printf("错误:空顺序表");
        element e = {0};
        return e;
    }
    return L.data[L.cnt-1];
}
void clist_modify(clist &L,int i,element e){
	//作用:修改位序为i的节点的值
    if(i <= 0 || i > L.cnt || clist_ifempty(L)){
		printf("错误:错误位置或空顺序表");
        return;
    }
	L.data[i-1] = e;
}

(7)清空/删除顺序表

void clist_clear(clist &L){
    //作用:清空顺序表
	L.data = realloc(L.data,0);
    L.cnt = 0;
}
void clist_destroy(clist &L){
    //作用:删除顺序表
    clist_clear(L);
    L.len = 0;
}

(8)插入

插入分为两种,一种是已知位置在其前插入,一种是已知位置在其后插入

■前.插入

插入位序范围:\([1,n+1]\)

顺序表由于节点间索引关系固定,只能通过不断修改节点来达成【移动】效果来插入

假如要插入元素位置为\(i\),那么需要将\([i,n]\)范围的元素整体移动到\([i+1,n+1]\)

这样就能腾挪出\(a[i]\)来放置元素

void clist_insertpre(clist &L,int pos,element e){
    //作用: 位序pos元素前.插入e
    if(pos < 1 || pos > L.cnt+1 || L.cnt+1 > L.len){
        printf("错误:位置错误或溢出");
        return;
    }
    L.cnt++;
    for(int i = L.cnt;i > pos;i--){
		L.data[i-1] = L.data[i-2];
    }
    L.data[pos-1] = e;
}

\(T(n)=O(cnt+1-pos)\)

■后.插入

插入位序范围:\([0,n]\)

后插入和前插入类似,可以简单理解为pos后插入等价于pos+1前插入

void clist_insertpost(clist &L,int pos,element e){
    //作用: 位序pos元素后.插入e
    if(pos < 0 || pos > L.cnt || L.cnt+1 > L.len){
        printf("错误:位置错误或溢出");
        return;
    }
    L.cnt++;
    for(int i = L.cnt;i > pos+1;i--){
		L.data[i-1] = L.data[i-2];
    }
    L.data[pos] = e;
}

\(T(n)=O(cnt-pos)\)

(9)删除

删除位序范围:\([1,n]\)

删除同样类似,如果要删除\(a[i]\),只需要将\([i+1,n]\)整体移动到\([i,n-1]\)覆盖掉即可

void clist_delete(clist &L,int pos){
    //作用:删除位序pos的节点
    if(pos < 1 || pos > L.cnt){
        printf("错误:位置错误");
        return;
    }
    L.cnt--;
    for(int i = pos;i <= L.cnt;i++){
		L.data[i-1] = L.data[i];
    }
}

其他操作

注:这里不演示排序、查找操作

(1)顺序表逆序

void clist_inverse(clist &L){
    //作用:顺序表逆序
	for(int i = 1;i <= L.cnt/2;i++){
        element t = L.data[i-1];
        L.data[i-1] = L.data[L.cnt-i];
        L.data[L.cnt-i] = L.data[i-1];
    }
}

\(T(n)=O(\frac{cnt}{2})\)

(2)合并有序顺序表

假设有L1、L2两个已经排序好的表(这里假设是不严格递增的序列),要将两个表进行合并保存在L3且依然为不严格递增序列

这本质上是要通过队列来解决,这里不进行扩展。

1.定义为L1[0]、L2[0]为最前元素

2.比较最前元素,谁小谁就保存于L3

3.被保存的最前元素往后推进,回至第二步

当然只要有一个顺序表的最前元素到底就说明这个顺序表不存在最前元素了,然后另外一个表剩下的元素只要逐个追加至L3即可

void clist_union(clist &L3,clist &L1,clist &L2){
	clist_create(L3,L1.cnt+L2.cnt);
    int front1 = 0,front2 = 0;
    while(front1 < L1.cnt && front2 < L2.cnt){
        if(L1.data[front1].value < L2.data[front2].value)
            clist_insertpre(L3,L3.cnt+1,L1.data[front1++]);
        else clist_insertpre(L3,L3.cnt+1,L2.data[front2++]);
    }
    if(front1 == L1.cnt) while(front2 < L2.cnt)
            clist_insertpre(L3,L3.cnt+1,L2.data[front2++]);
    else while(front1 < L1.cnt)
            clist_insertpre(L3,L3.cnt+1,L1.data[front1++]);
}

当然这里可以对问题进行扩展,可以合并n个顺序表,这时就需要对n个最前元素进行比较,同时还可能需要stdarg库来实现

//伪代码
void clist_union(clist *L,...){
    //索引参数表所有顺序表长度累加得出L的表长sum和顺序表个数n
    //创建L表,长度为sum
    
    //这里参数应该为队列,队列中包含着“最前元素”
    //然后定义n个最前元素
    
    //和之前步骤类似,不过要比较的是所有未达到最后的“最前元素”
    //然后即可得到
}


参考教程

C语言技术网 数据结构

原文地址:https://www.cnblogs.com/AlienfronNova/p/15595535.html