1 - 顺序表的概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改等功能。
- 顺序表一般可以分为:
- 静态顺序表:使用定长数组存储元素。
- 动态顺序表:使用动态开辟的数组存储。
而现实的顺序表大多数采用动态的,因为静态顺序表有几个缺点,当然还有优点.我们下面就展开讲讲
静态顺序表缺点:
- 插入和删除操作需要移动大量的元素
- 当线性表长度变化较大时,难以确定存储空间的容量
- 造成存储空间的 “碎片”
静态顺序表优点:
- 可以快速访问任一元素
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小。
相对总体来说静态顺序表是不够优的,所有该文章我们就讲解动态顺序表,当然最后还会附上静态顺序表的全代码.
- 存储顺序表结构需要三个属性
- 存储空间的起始位置:数据data .
- 线性表的存储容量: capacity
- 线性表的当前长度: sz
struct SeqListNode { STDataType* data; int sz; //顺序表元素个数 int capacity; //顺序表容量 };
2 - 动态顺序表接口实现
存储结构三个属性
typedef int STDataType; //方便以后要存储其它类型。 //动态通讯录 typedef struct SeqListNode { STDataType* data; //动态开辟的数组 int sz; //顺序表元素个数 int capacity; //顺序表容量 }SeqNode;
初始化顺序表
//初始化顺序表 void Seqinit(SeqNode* head) { assert(head); //断言:判断是否为空指针 head->data = NULL; //首先指向NULL head->sz = 0; //初始顺序个数为0 head->capacity = 0; //初始容量为0 }
顺序表增容
顺序表添加元素时,都需检测一下是否要增容。不然会出现越界情况。
每次扩容为原来的2倍,如扩容太大会浪费空间。
void CheckCapacity(SeqNode* head) { //断言:判断是否为空指针 assert(head); int newcapacity = 0;//新容量 if (head->sz == head->capacity) { if (head->capacity == 0) newcapacity = head->capacity = 4; //首次扩容为4 else newcapacity = head->capacity * 2; //扩容为原来的2倍 //扩容 STDataType* tmp = NULL; if (head->data == NULL) tmp = (STDataType*)malloc(newcapacity * sizeof(STDataType)); //首次扩容 else tmp = (STDataType*)realloc(head->data, sizeof(STDataType) * newcapacity); //再次扩容 //检测扩容是否成功 if (tmp == NULL) { //如果扩容失败就退出程序 perror(" Capacityfile: "); exit(-1); } //扩容成功把内容给回顺序表属性 head->data = tmp; head->capacity = newcapacity; } }
顺序表尾插
//顺序表尾插 void SeqPushBack(SeqNode* head, STDataType x) { assert(head); //断言:判断是否为空指针 CheckCapacity(head); //检测是否需要扩容 head->data[head->sz] = x; //要插入的数据 head->sz++; //顺序表容量+1 }
顺序表头插
头插算法思路
- 从最后个元素开始向前遍历到第0个位置,分别向后挪动一个位置.
- 将要插入元素填入首元素位置处
- 表长+1.
//顺序表头插 void SeqPushFront(SeqNode* head, STDataType x) { assert(head);//断言:判断是否为空指针 CheckCapacity(head); //检测是否需要扩容 //从前往后递推覆盖 int i = head->sz - 1; for (i; i >= 0; i--) { head->data[i + 1] = head->data[i]; //将数据【0 - sz-1】向后递推覆盖 } head->data[0] = x; //进行头插 head->sz++; //顺序表容量+1 }
顺序表尾删
直接把表长减一即可
//顺序表尾删 void SeqPopBack(SeqNode* head) { assert(head);//断言:判断是否为空指针 if (head->sz == 0) //判断顺序表是否有元素 { printf("该顺序表为空,无法删除\n"); return; } head->sz--; //顺序表容量-1 }
顺序表头删
- 从第二个元素开始到最后一个元素,分别将他们都向前移动一个位置。
- 表长在减1。
//顺序表头删 void SeqPopFront(SeqNode* head) { assert(head);//断言:判断是否为空指针 if (head->sz == 0) //判断顺序表是否有元素 { printf("该顺序表为空,无法删除\n"); return; } //从后往前递推覆盖 int i = 0; for (i = 0; i > head->sz-1; i--) { head->data[i] = head->data[i + 1]; //将数据从【1-sz-1】向前覆盖 } head->sz--; //顺序表容量-1 }
顺序表查找
只要遍历顺序表找到需要查找的值,如果找到返回他的下标即可.
找不到返回-1,
int SeqFind(const SeqNode* head, STDataType x) { assert(head);//断言:判断是否为空指针 if (head->sz == 0) //判断顺序表是否有元素 { printf("该顺序表为空,无法查找\n"); return -1; } int i = 0; for (i = 0; i < head->sz; i++) { if (head->data[i] == x) //查找到,就返回该元素下标 { return i; } } return -1; //查找失败 返回-1 }
顺序表中删除指定下标数据
删除算法思路:
- 如果删除位置不合理,抛出异常.
- 从删除元素位置开始遍历到最后一个元素位置,分别将他们都向前挪动一个位置.
- 表长减1
//在顺序表中删除指定下标位置的数据 void SeqErase(SeqNode* head, int pos) { assert(head);//断言:判断是否为空指针 if (pos < 0 || head->sz == 0) { printf(" 要删除下标非法或者顺序表为空,删除失败\n"); return ; } int i = pos; //将要删除的位置把后面的覆盖过来,最后在减 for (i; i < head->sz-1; i++) { head->data[i] = head->data[i+1]; //将【pos+1 - sz-1】的数据向前挪动一个位置 } head->sz--;//顺序表容量-1 }
在顺序表指定下标位置插入数据
插入算法的思路
- 如果删除位置不合理,抛出异常
- 从最后一个元素开始向前遍历到要插入的位置,分别将他们都向后移动一个位置
- 将元素添加到要插入的位置
- 表长+1
void SeqInsert(SeqNode* head, int pos, STDataType x) { assert(head); //断言:判断是否为空指针 void Seqmodify(SeqNode* head, int pos, STDataType x); if (pos < 0 ) { printf(" 下标非法,删除失败\n"); return; } CheckCapacity(head); //检测是否需要扩容 //将顺序表【pos - sz-1】从最后一个元素开始依次挪动到后一位 int i = head->sz-1; for (i; i >= pos; i--) { head->data[i+1] = head->data[i]; //依次挪动到后一位,直到pos位置挪动到后一位 } head->data[pos] = x; //插入数据 head->sz++; //顺序表容量+1 }
修改指定下标的值
//修改指定下标的值 void Seqmodify(SeqNode* head, int pos, STDataType x) { assert(head);//断言:判断是否为空指针 if (pos < 0 || head->sz == 0) { printf(" 要修改的下标非法或者顺序表为空,修改失败\n"); return; } head->data[pos] = x;//修改数据; }
获得顺序表个数
//查顺序表中有效数据个数 int Seqsize(const SeqNode* head) { assert(head);//断言:判断是否为空指针 return head->sz; //返回顺序表有效数据个数 }
顺序表打印
//顺序表打印 void Seqprint(const SeqNode* head) { assert(head); //断言:判断是否为空指针 if (head->sz == 0) { printf("顺序表为空,无法打印\n"); return; } int i = 0; for (i = 0; i < head->sz; i++) { printf("%d ", head->data[i]); } printf("\n"); }
顺序表销毁
//顺序表销毁 void SeqDestroy(SeqNode* head) { assert(head); //断言:判断是否为空指针 free(head->data); head->data = NULL; head->sz = 0; head->capacity = 0; }
3 - 动态顺序表全代码
SeqLish.h
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<assert.h> typedef int STDataType; //动态通讯录 typedef struct SeqListNode { STDataType* data; int sz; //顺序表元素个数 int capacity; //顺序表容量 }SeqNode; //初始化链表 void Seqinit(SeqNode* head); //增容 void CheckCapacity(SeqNode* head); //顺序表尾插 void SeqPushBack(SeqNode* head, STDataType x); //顺序表打印 void Seqprint(const SeqNode* head); //顺序表销毁 void SeqDestroy(SeqNode* head); //顺序表头插 void SeqPushFront(SeqNode* head, STDataType x); //顺序表尾删 void SeqPopBack(SeqNode* head); //顺序表头删 void SeqPopFront(SeqNode* head); //顺序表查找 int SeqFind(const SeqNode* head, STDataType x); //在顺序表中删除指定下标位置的数据 void SeqErase(SeqNode* head, int pos); //在顺序表指定下标位置插入数据 void SeqInsert(SeqNode* head, int pos, STDataType x); //修改指定下标的值 void Seqmodify(SeqNode* head, int pos, STDataType x); //查顺序表中有效数据个数 int Seqsize(const SeqNode* head);
SeqLish.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"SeqList.h" //初始化链表 void Seqinit(SeqNode* head) { head->data = NULL; //指向动态开辟的数组 head->sz = 0; //初始顺序个数为0 head->capacity = 0; //初始容量为0 } //顺序表销毁 void SeqDestroy(SeqNode* head) { assert(head); //断言:判断是否为空指针 free(head->data); head->data = NULL; head->sz = 0; head->capacity = 0; } //增容 void CheckCapacity(SeqNode* head) { //断言:判断是否为空指针 assert(head); int newcapacity = 0;//新容量 if (head->sz == head->capacity) { if (head->capacity == 0) newcapacity = head->capacity = 4; //首次扩容为4 else newcapacity = head->capacity * 2; //扩容为原来的2倍 //扩容 STDataType* tmp = NULL; if (head->data == NULL) tmp = (STDataType*)malloc(newcapacity * sizeof(STDataType)); //首次扩容 else tmp = (STDataType*)realloc(head->data, sizeof(STDataType) * newcapacity); //再次扩容 //检测扩容是否成功 if (tmp == NULL) { //如果扩容失败就退出程序 perror(" Capacityfile: "); exit(-1); } //扩容成功 head->data = tmp; head->capacity = newcapacity; } } //顺序表尾插 void SeqPushBack(SeqNode* head, STDataType x) { assert(head); //断言:判断是否为空指针 CheckCapacity(head); //检测是否需要扩容 head->data[head->sz] = x; //要插入的数据 head->sz++; //顺序表容量+1 } //顺序表打印 void Seqprint(const SeqNode* head) { assert(head); //断言:判断是否为空指针 if (head->sz == 0) { printf("顺序表为空,无法打印\n"); return; } int i = 0; for (i = 0; i < head->sz; i++) { printf("%d ", head->data[i]); } printf("\n"); } //顺序表头插 void SeqPushFront(SeqNode* head, STDataType x) { assert(head);//断言:判断是否为空指针 CheckCapacity(head); //检测是否需要扩容 //从前往后递推覆盖 int i = head->sz - 1; for (i; i >= 0; i--) { head->data[i + 1] = head->data[i]; //将数据【0 - sz-1】向后递推覆盖 } head->data[0] = x; //进行头插 head->sz++; //顺序表容量+1 } //顺序表尾删 void SeqPopBack(SeqNode* head) { assert(head);//断言:判断是否为空指针 if (head->sz == 0) //判断顺序表是否有元素 { printf("该顺序表为空,无法删除\n"); return; } head->sz--; //顺序表容量-1 } //顺序表头删 void SeqPopFront(SeqNode* head) { assert(head);//断言:判断是否为空指针 if (head->sz == 0) //判断顺序表是否有元素 { printf("该顺序表为空,无法删除\n"); return; } //从后往前递推覆盖 int i = 0; for (i = 0; i > head->sz-1; i--) { head->data[i] = head->data[i + 1]; //将数据从【1-sz-1】向前覆盖 } head->sz--; //顺序表容量-1 } //顺序表查找 int SeqFind(const SeqNode* head, STDataType x) { assert(head);//断言:判断是否为空指针 if (head->sz == 0) //判断顺序表是否有元素 { printf("该顺序表为空,无法查找\n"); return -1; } int i = 0; for (i = 0; i < head->sz; i++) { if (head->data[i] == x) //查找到,就返回该元素下标 { return i; } } return -1; //查找失败 返回-1 } //在顺序表中删除指定下标位置的数据 void SeqErase(SeqNode* head, int pos) { assert(head);//断言:判断是否为空指针 if (pos < 0 || head->sz == 0) { printf(" 要删除下标非法或者顺序表为空,删除失败\n"); return ; } int i = pos; //将要删除的位置把后面的覆盖过来,最后在减 for (i; i < head->sz-1; i++) { head->data[i] = head->data[i+1]; //将【pos+1 - sz-1】的数据向前挪动一个位置 } head->sz--;//顺序表容量-1 } //在顺序表指定下标位置插入数据 void SeqInsert(SeqNode* head, int pos, STDataType x) { assert(head); //断言:判断是否为空指针 void Seqmodify(SeqNode* head, int pos, STDataType x); if (pos < 0 ) { printf(" 下标非法,删除失败\n"); return; } CheckCapacity(head); //检测是否需要扩容 //将顺序表【pos - sz-1】从最后一个元素开始依次挪动到后一位 int i = head->sz-1; for (i; i >= pos; i--) { head->data[i+1] = head->data[i]; //依次挪动到后一位,直到pos位置挪动到后一位 } head->data[pos] = x; //插入数据 head->sz++; //顺序表容量+1 } //修改指定下标的值 void Seqmodify(SeqNode* head, int pos, STDataType x) { assert(head);//断言:判断是否为空指针 if (pos < 0 || head->sz == 0) { printf(" 要修改的下标非法或者顺序表为空,修改失败\n"); return; } head->data[pos] = x;//修改数据; } //查顺序表中有效数据个数 int Seqsize(const SeqNode* head) { assert(head);//断言:判断是否为空指针 return head->sz; //返回顺序表有效数据个数 }
4 - 静态顺序表全代码
SeqLish.h
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<assert.h> #define Max 100 typedef int STDataType; //静态顺序表 typedef struct SeqListNode { STDataType data[Max]; int sz; //顺序表数量 int capacity; //顺序表容量 }SeqNode; //初始化 void Seqinit(SeqNode* head); //增容 void CheckCapacity(SeqNode* head); //顺序表尾插 void SeqPushBack(SeqNode* head, STDataType x); //顺序表头插 void SeqPushFront(SeqNode* head, STDataType x); //顺序表尾删 void SeqPopBack(SeqNode* head); //顺序表头删 void SeqPopFront(SeqNode* head); //顺序表打印 void Seqprint(const SeqNode* head); //顺序表销毁 void SeqDestory(SeqNode* head); //顺序表查找 int SeqFind(const SeqNode* head, STDataType x); //在顺序表中删除指定下标位置的数据 void SeqErase(SeqNode* head, int pos); //在顺序表指定下标位置插入数据 void SeqInsert(SeqNode* head, int pos, STDataType x); //修改指定下标的值 void Seqmodify(SeqNode* head, int pos, STDataType x); //查顺序表中有效数据个数 int Seqsize(const SeqNode* head);
SeqLish.c
#include"SeqList.h" //顺序表初始化 void Seqinit(SeqNode* head) { assert(head); head->capacity = 0; head->sz = 0; } //是否需要增容 void CheckCapacity(SeqNode* head) { assert(head); if (head->sz == head->capacity) { if (head->capacity == Max) { perror("通讯录已满,无法增容:"); exit(-1); } if (head->capacity == 0) head->capacity = 4; else { head->capacity *= 2; if (head->capacity > Max) head->capacity = Max; } } } //顺序表打印 void Seqprint(const SeqNode* head) { assert(head); if (head->sz == 0) { perror("顺序表元素为空,请重新增加元素\n"); return; } int i = 0; for (i = 0; i < head->sz; i++) { printf("%d ", head->data[i]); } printf("\n"); } //顺序表尾插 void SeqPushBack(SeqNode* head, STDataType x) { assert(head); CheckCapacity(head); head->data[head->sz] = x; head->sz++; } //顺序表头插 void SeqPushFront(SeqNode* head, STDataType x) { assert(head); CheckCapacity(head); int i = head->sz - 1; for (i; i >= 0; i--) { head->data[i + 1] = head->data[i]; } head->data[0] = x; head->sz++; } //顺序表尾删 void SeqPopBack(SeqNode* head) { assert(head); head->sz--; } //顺序表头删 void SeqPopFront(SeqNode* head) { assert(head); int i = 0; for (i = 1; i < head->sz; i++) { head->data[i-1] = head->data[i]; } head->sz--; } //顺序表销毁 void SeqDestory(SeqNode* head) { head->capacity = 0; head->sz = 0; } //顺序表查找 int SeqFind(const SeqNode* head, STDataType x) { assert(head); if (head->sz == 0) { perror("顺序表为空\n"); return -1; } int i = 0; for (i = 0; i < head->sz; i++) { //找到返回下标 if (head->data[i] == x) { return i; } } return -1; } //在顺序表中删除指定下标位置的数据 void SeqErase(SeqNode* head, int pos) { assert(head); if (head->sz == 0) { perror("顺序表为空\n"); return; } int i = pos; for (i; i < head->sz-1; i++) { head->data[i] = head->data[i + 1]; } head->sz--; } //在顺序表指定下标位置插入数据 void SeqInsert(SeqNode* head, int pos, STDataType x) { assert(head); CheckCapacity(head); int i = head->sz - 1;; for (i; i >= pos ; i--) { head->data[i+1] = head->data[i]; } head->data[pos] = x; head->sz++; } //修改指定下标的值 void Seqmodify(SeqNode* head, int pos, STDataType x) { assert(head); if (head->sz == 0) { perror("顺序表为空,无法修改\n"); return; } head->data[pos] = x; } //查顺序表中有效数据个数 int Seqsize(const SeqNode* head) { assert(head); return head->sz; }