一、线性表
线性表是最常用且简单的一种数据结构。简言之,一个线性表是n个数据元素的有限序列。至于每个数据元素的具体含义,在不同的情况下各不相同,它可以是一个整数或一个字符,也可以是一页书,甚至其他更复杂的信息。
线性表按其数据的存储方式分为两种:
- 顺序表:数据结点之间,逻辑相邻,物理也相邻;
- 链表:数据结点之间,逻辑相邻,物理不一定相邻。
二、顺序表
2-1 结构设计
固定大小的顺序表的结构设计
#define SEQ_INIT_SIZE 10
typedef int ElemType;//数组元素类型
typedef struct SeqList {
ElemType data[SEQ_INIT_SIZE];//固定大小的连续存储空间
int size;//当前有效元素个数
}SeqList;
可变大小的顺序表的结构设计
typedef int ElemType;//数组元素类型
typedef struct SeqList {
ElemType* data;//数组起始地址
int length; //数组总容量
int size; //有效数据个数
}SeqList;
2-2 函数实现接口声明
//1.初始化
void initSeqList(SeqList* plist);
//2.销毁 free
void DestoryList(SeqList* plist);
//3.清空 size=0
void ClearList(SeqList* plist);
//4.扩容操作
Status grow(SeqList* plist);
//5.打印数组元素
void Show(SeqList* plist);
//6.判满操作
Status IsFull(SeqList* plist);
//7.判空操作
Status IsEmpty(SeqList* plist);
//8.头插法
Status InsertHead(SeqList* plist,ElemType val);
//9.尾插法
Status InsertTail(SeqList* plist,ElemType val);
//10.指定位置插入 pos---下标
Status InsertPosVal(SeqList* plist,int posindex, ElemType val);
//11.头删法
Status DeleteHead(SeqList* plist);
//12.尾删法
Status DeleteTail(SeqList* plist);
//13.指定位置删除 posindex---下标
Status DeletePos(SeqList* plist, int posindex);
//14.删除指定元素
Status DeleteVal(SeqList* plist,ElemType val);
//15.查询某个元素返回下标
int SearchVal(SeqList* plist, ElemType val);
//16.冒泡排序
void BubbleSort(SeqList* plist);
//17.二分查找(前提数组完全有序)---非递归
int BinarySearch(SeqList* plist, ElemType val);
//18.二分查找(前提数组完全有序)---递归
int recursionBinarySearch(SeqList* plist, ElemType val);
//19.将两个有序顺序表合并为一个有序的顺序表
void MergeList(const SeqList* pla, const SeqList* plb, SeqList* plc);
2-3 头文件设计(seqlist.h)
//#pragma once
//防止头文件被重复引用
#ifndef SEQLIST_H
#define SEQLIST_H
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -1 //内存溢出
#define LIST_INIT_CAPACITY 10//数组初始化大小
#define LIST_GROW 2//扩容倍数
typedef int Status;//状态 TRUE FALSE
typedef int ElemType;//数组元素类型
//不定长顺序表
typedef struct SeqList {
ElemType* data;//数组起始地址
int length; //数组总容量
int size; //有效数据个数
}SeqList;
//函数实现接口声明
//1.初始化
void initSeqList(SeqList* plist);
//2.销毁 free
void DestoryList(SeqList* plist);
//3. ......
#endif // !SEQLIST_H
防止头文件重复引用
#ifndef SEQLIST_H //防止seqliat.h被重复引用
#define SEQLIST_H
//......
#endif // !SEQLIST_H
“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。
#pragma once是编译相关,不一定在每个编译系统上都可以用,移植性差,不过现在基本已经每个编译器都有这个定义了。
#ifndef,#define,#endif 这个是C++语言相关,通过宏定义避免文件多次编译。在所有支持C++语言的编译器上都是有效的,如果程序要跨平台,最好使用这种方式。
2-4 接口实现(seqlist.cpp)
#include<stdio.h>
#include "seqlist.h"
#include <assert.h>
#include <malloc.h>
1.顺序表初始化
void initSeqList(SeqList* plist) {
assert(plist != NULL);
plist->data = (ElemType*)malloc(LIST_INIT_CAPACITY * sizeof(ElemType));
if (plist->data == NULL)return;
//memset(plist->data, 0, sizeof(ElemType));
plist->length = LIST_INIT_CAPACITY;//数组初始化长度
plist->size = 0;
}
2.销毁操作
void DestoryList(SeqList* plist) {
assert(plist != NULL);
free(plist->data);//free 释放堆内存
plist->data = NULL;//防止野指针(悬挂指针)
}
3.清空操作
//删除有效数据
void ClearList(SeqList* plist) {
assert(plist != NULL);
plist->size = 0;
}
4.打印数组元素
void Show(SeqList* plist) {
assert(plist != NULL);
int n = plist->size;
for (int i = 0; i < n; i++) {
printf("%5d", plist->data[i]);
}
printf("\n");
}
5.扩容操作
Status grow(SeqList* plist) {
assert(plist != NULL);
if (plist == NULL)return ERROR;
int newlength = plist->length * LIST_GROW;
ElemType* p;
p = (ElemType*)realloc(plist->data, newlength * sizeof(ElemType));
if (p == NULL) return OVERFLOW;
plist->data = p;
plist->length = newlength;
return OK;
}
6.判满操作
Status IsFull(SeqList* plist) {
assert(plist != NULL);
return plist->length == plist->size;
}
7.判空操作
Status IsEmpty(SeqList* plist) {
assert(plist != NULL);
return plist->size == 0;
}
8.头插法
Status InsertHead(SeqList* plist, ElemType val) {
assert(plist != NULL);
if (IsFull(plist) && grow(plist) != OK) {
return OVERFLOW;
}
for (int i = plist->size - 1; i >= 0; i--) {
plist->data[i + 1] = plist->data[i];
}
plist->data[0] = val;
plist->size++;
return OK;
}
9.尾插法
Status InsertTail(SeqList* plist, ElemType val) {
assert(plist != NULL);
if (IsFull(plist) && grow(plist) != OK) {
return OVERFLOW;
}
plist->data[plist->size] = val;
plist->size++;
return OK;
}
10.指定位置插入 pos—下标
Status InsertPosVal(SeqList* plist, int posindex, ElemType val) {
assert(plist != NULL);
if (posindex<0 && posindex>plist->size) {
return ERROR;
}
if (IsFull(plist) && grow(plist) != OK) {
return OVERFLOW;
}
//从posindex后续数据向后移动
for (int i = plist->size - 1; i >= posindex; i--) {
plist->data[i + 1] = plist->data[i];
}
plist->data[posindex] = val;
plist->size++;
return OK;
}
11.头删法
Status DeleteHead(SeqList* plist) {
//assert(plist != NULL);
//if (IsEmpty(plist))return FALSE;
数据元素向前移动 覆盖前一个
//for (int i = 1; i < plist->size; i++) {
// plist->data[i - 1] = plist->data[i];
//}
//plist->size--;
//return OK;
return DeletePos(plist, 0);
}
头删法就是删除下标为0的数据元素。
12.尾删法
Status DeleteTail(SeqList* plist) {
//assert(plist != NULL);
//if (IsEmpty(plist))return FALSE;
//plist->size--;//局限
//return OK;
return DeletePos(plist, plist->size - 1);
}
尾删法就是删除下标为plist->size - 1的数据元素。
13.指定位置删除 posindex—下标
Status DeletePos(SeqList* plist, int posindex) {
assert(plist != NULL);
if (IsEmpty(plist)|| posindex > plist->size|| posindex < 0)return FALSE;
for (int i = posindex; i < plist->size - 1; i++) {
plist->data[i] = plist->data[i+1];
}
plist->size--;
return OK;
}
14.删除指定元素
Status DeleteVal(SeqList* plist, ElemType val) {
assert(plist != NULL);
int index;
while ((index = SearchVal(plist, val))>=0) {
DeletePos(plist, index);
}
return OK;
}
首先找到元素所在下标,接着按照指定位置删除。
15.查询某个元素返回下标
int SearchVal(SeqList* plist, ElemType val) {
assert(plist != NULL);
int index = -1;
for (int i = 0; i < plist->size; i++) {
if (plist->data[i] == val) {
index = i;
break;
}
}
return index;
}
16.冒泡排序
void Swap1(SeqList* plist, int index1, int index2) {
assert(plist != nullptr);
if (index1 < 0 || index2 < 0 || index1 >= plist->size || index2 >= plist->size) {
return ;//exit(EXIT_FALTURE)
}
ElemType temp = plist->data[index1];
plist->data[index1] = plist->data[index2];
plist->data[index2] = temp;
}
void Swap2(ElemType* p1, ElemType* p2) {
assert(p1 != nullptr && p2!=nullptr);
ElemType temp = *p1;
*p1 = *p2;
*p2 = temp;
}
//冒泡排序
void BubbleSort(SeqList* plist) {
//assert(plist != NULL);
assert(plist != nullptr);
bool flag;
for (int i = 0; i < plist->size - 1; i++) {//控制趟数
flag = false;
for (int j = 0; j < plist->size - 1 - i; j++) {
if (plist->data[j] > plist->data[j + 1]) {
//Swap1(plist, j, j + 1); 其他语言
Swap2(&plist->data[j], &plist->data[j + 1]);//C语言写法
flag = true;
}
}
if (!flag) {
break;
}
}
}
17.二分查找(前提数组完全有序)—非递归
//非递归二分查找 时间复杂度:O(log2n) 空间复杂度:O(1)
int BinarySearch(SeqList* plist, ElemType val) {
assert(plist != nullptr);
int left = 0,right=plist->size-1;
int index = -1;//返回元素下标
while (left < right) {
int middle = (left + right) / 2;
//int middle = (right - left) / 2 + left;
//int middle = ((right - left >> 1)) + left;
if (plist->data[middle] > val) {
right = middle - 1;
}
else if (plist->data[middle] < val) {
left = middle + 1;
}
else {
index = middle;
}
}
return index;
}
18.二分查找(前提数组完全有序)—递归
//递归二分查找 (自己调用自己 退出条件 问题规模)空间复杂度:O(log2n) 空间:O(log2n)
int SearchSection(SeqList* plist, int left, int right, ElemType val) {
if (left > right) return -1;
int middle = (left + right) / 2;
if (plist->data[middle] == val)return middle;
if (plist->data[middle] > val) {
return SearchSection(plist, left, middle - 1, val);
}
else {
return SearchSection(plist, middle+1, right, val);
}
}
int recursionBinarySearch(SeqList* plist, ElemType val) {
assert(plist != nullptr);
return SearchSection(plist, 0, plist->size-1, val);
}
19.将两个有序顺序表合并为一个有序的顺序表
void MergeList(const SeqList* pla, const SeqList* plb, SeqList* plc) {
assert(pla != NULL && plb != NULL && plc != NULL);
int i = 0, j = 0;
while (i < pla->size && j < plb->size) {
if (pla->data[i] > plb->data[j]) {
//plc->data[x++] = plb->data[j++];
InsertTail(plc, plb->data[j++]);
}
else {
//plc->data[x++] = pla->data[i++];
InsertTail(plc, pla->data[i++]);
}
}
if (i >= pla->size) {//pla走完,剩余plb
while (j < plb->size) {
//plc->data[x++] = plb->data[j++];
InsertTail(plc, plb->data[j++]);
}
}
if (j >= plb->size) {//plb走完,剩余pla
while (i < pla->size) {
//plc->data[x++] = pla->data[i++];
InsertTail(plc, pla->data[i++]);
}
}
}