数据结构学习笔记(数据结构概念、顺序表的增删查改等)详细整理
数据结构概念
1.什么是数据结构? 数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。 2.什么是算法? 算法(Algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果。 3.算法效率 算法效率分析分为两种:第一种是时间效率,第二种是空间效率。 时间复杂度:算法中的基本操作的执行次数,为算法的时间复杂度。 空间复杂度:空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度 。 实际中我们计算时间和空间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。
顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。 顺序表一般可以分为: 1. 静态顺序表:使用定长数组存储。 2. 动态顺序表:使用动态开辟的数组存储。 线性表每个元素必须有前驱和后继
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
动态顺序表接口实现
**1.顺序表初始化**
void SeqListInit(SeqList *pst, size_t capacity)
{
pst->capacity = capacity;
pst->base = (ElemType*)malloc(sizeof(ElemType) * pst->capacity);
assert(pst->base != NULL);
memset(pst->base, 0, sizeof(ElemType)*pst->capacity);
pst->size = 0;
}
**2.尾插**
void SeqListPushBack(SeqList *pst, ElemType v)
{
if(_IsFull(pst) && !_Inc(pst))
{
printf("顺序表容量不足,%d 不能插入.\n", v);
return;
}
pst->base[pst->size++] = v;
}
**头插**
void SeqListPushFront(SeqList *pst, ElemType v)
{
if(_IsFull(pst) && !_Inc(pst))
{
printf("顺序表容量不足,%d 不能插入.\n", v);
return;
}
for(int i=pst->size; i>0; --i)
{
pst->base[i] = pst->base[i-1];
}
pst->base[0] = v;
pst->size++;
}
**3.存储数据显示**
void SeqListShow(SeqList *pst)
{
for(int i=0; i<pst->size; ++i)
printf("%d ", pst->base[i]);
printf("\n");
}
**4.尾删**
void SeqListPopBack(SeqList *pst)
{
if(_IsEmpty(pst))
{
printf("顺序表已空,不能删除.\n");
return;
}
pst->size--;
}
**5.头删**
void SeqListPopFront(SeqList *pst)
{
if(_IsEmpty(pst))
{
printf("顺序表已空,不能删除.\n");
return;
}
for(int i=0; i<pst->size-1; ++i)
pst->base[i] = pst->base[i+1];
pst->size--;
}
**6.按位置插入数据**
void SeqListInsertByPos(SeqList *pst, int pos, ElemType v)
{
if(_IsFull(pst) && !_Inc(pst))
{
printf("顺序表容量不足,%d 不能插入.\n", v);
return;
}
if(pos<0 || pos>pst->size)
{
printf("插入的位置非法, %d 不能插入.\n", v);
return;
}
for(int i=pst->size; i>pos; --i)
pst->base[i] = pst->base[i-1];
pst->base[pos] = v;
pst->size++;
}
**7.按值插入**
void SeqListInsertByVal(SeqList *pst, ElemType v)
{
int pos = 0;
while(pos<pst->size && v>pst->base[pos])
pos++;
SeqListInsertByPos(pst, pos, v);
}
void SeqListEraseByPos(SeqList *pst, int pos)
{
if(pos<0 || pos>=pst->size)
{
printf("删除的位置非法,不能删除数据.\n");
return;
}
for(int i=pos; i<pst->size-1; ++i)
pst->base[i] = pst->base[i+1];
pst->size--;
}
**8.按位置删除**
void SeqListEraseByPos(SeqList *pst, int pos)
{
if(pos<0 || pos>=pst->size)
{
printf("删除的位置非法,不能删除数据.\n");
return;
}
for(int i=pos; i<pst->size-1; ++i)
pst->base[i] = pst->base[i+1];
pst->size--;
}
**9.按值删除**
void SeqListEraseByVal(SeqList *pst, ElemType key)
{
int pos = SeqListFindByVal(pst, key);
if(pos == -1)
{
printf("要删除的数据不存在.\n");
return;
}
SeqListEraseByPos(pst, pos);
}
**10.按位置查找** **11按值查找**
ElemType SeqListFindByPos(SeqList *pst, int pos)
{
assert(pos>=0 && pos<pst->size);
return pst->base[pos];
}
int SeqListFindByVal(SeqList *pst, ElemType key)
{
for(int i=0; i<pst->size; ++i)
{
if(key == pst->base[i])
return i;
}
return -1;
}
**12.排序**
void SeqListSort(SeqList *pst)
{
for(int i=0; i<pst->size-1; ++i)
{
bool is_swap = false;
for(int j=0; j<pst->size-i-1; ++j)
{
if(pst->base[j] > pst->base[j+1])
{
Swap(&(pst->base[j]), &(pst->base[j+1]));
is_swap = true;
}
}
if(!is_swap)
break;
}
}
**13.逆置**
void SeqListReverse(SeqList *pst)
{
int left = 0;
int right = pst->size-1;
while(left < right)
{
Swap(&(pst->base[left]), &(pst->base[right]));
left++;
right--;
}
}
**14.顺序表长度**
size_t SeqListLength(SeqList *pst)
{
return pst->size;
}
**15.顺序表容量 16.清楚** 17.摧毁
size_t SeqListCapacity(SeqList *pst)
{
return pst->capacity;
}
void SeqListClear(SeqList *pst)
{
pst->size = 0;
}
void SeqListDestroy(SeqList *pst)
{
free(pst->base);
pst->base = NULL;
pst->capacity = pst->size = 0;
}
我们一开始设置了顺序表的大小,但是容量是有限的,当插入数据超过这个容量时候就插不进去了,当然也不能一开始就设定很大的容量,这样会导致空间的浪费,因此需要让它根据需要动态的去扩容。
**容量扩展**
bool _Inc(SeqList *pst)
{
pst->base = (ElemType *)realloc(pst->base, sizeof(ElemType) * (pst->capacity+INC_SIZE));
if(pst->base == NULL)
return false;
pst->capacity += INC_SIZE;
return true;
}
bool _IsFull(SeqList *pst)
{
return pst->size >= pst->capacity;
}
bool _IsEmpty(SeqList *pst)
{
return pst->size == 0;
}
动态顺序表接口扩展
删除排序数组中的重复项
合并两个有序数组
动态顺序表的优缺点
优点: 无需为表示表中元素之间的逻辑关系而增加额外的存储空间可以快速地存取 表中任一位置的元素。 缺点: 插入删除操作需要移动大量元素当线性表长度变化较大时,难以确定存储空间的容量 造成存储空间的碎片。
完整代码
**main.c**
#include"SeqList.h"
int main(int argc, char *argv[])
{
SeqList mylist;
SeqListInit(&mylist, 8);
ElemType item, value, key;
int pos;
int select = 1;
while(select)
{
printf("********************************************\n");
printf("* [1] push_back [2] push_front *\n");
printf("* [3] show_list [0] quit_system *\n");
printf("* [4] pop_back [5] pop_front *\n");
printf("* [6] insert_pos [7] insert_val *\n");
printf("* [8] erase_pos [9] erase_val *\n");
printf("* [10] find_pos [11] find_val *\n");
printf("* [12] sort [13] reverse *\n");
printf("* [14] length [15] capacity *\n");
printf("* [16] clear [*17] destroy *\n");
printf("********************************************\n");
printf("请选择:>");
scanf("%d", &select);
if(select == 0)
break;
switch(select)
{
case 1:
printf("请输入要插入的数据<以-1结束>:>");
while(scanf("%d", &item), item!=-1)
{
SeqListPushBack(&mylist, item);
}
break;
case 2:
printf("请输入要插入的数据<以-1结束>:>");
while(scanf("%d", &item), item!=-1)
{
SeqListPushFront(&mylist, item);
}
break;
case 3:
SeqListShow(&mylist);
break;
case 4:
SeqListPopBack(&mylist);
break;
case 5:
SeqListPopFront(&mylist);
break;
case 6:
printf("请输入要插入的位置:>");
scanf("%d", &pos);
printf("请输入要插入的值:>");
scanf("%d", &item);
SeqListInsertByPos(&mylist, pos, item);
break;
case 7:
printf("请输入要插入的值:>");
scanf("%d", &item);
SeqListInsertByVal(&mylist, item);
break;
case 8:
printf("请输入要删除的位置:>");
scanf("%d", &pos);
SeqListEraseByPos(&mylist, pos);
break;
case 9:
printf("请输入要删除的值:>");
scanf("%d", &key);
SeqListEraseByVal(&mylist, key);
break;
case 10:
printf("请输入要查找的位置:>");
scanf("%d", &pos);
value = SeqListFindByPos(&mylist, pos);
printf("在%d位置的值为:> %d\n", pos, value);
break;
case 11:
printf("请输入要查找的值:>");
scanf("%d", &key);
pos = SeqListFindByVal(&mylist, key);
if(pos == -1)
printf("要查找的%d数据不存在.\n", key);
else
printf("要查找的数据%d在 %d位置.\n", key, pos);
break;
case 12:
SeqListSort(&mylist);
break;
case 13:
SeqListReverse(&mylist);
break;
case 14:
printf("顺序表的长度为:> %d\n", SeqListLength(&mylist));
break;
case 15:
printf("顺序表的容量为:> %d\n", SeqListCapacity(&mylist));
break;
case 16:
SeqListClear(&mylist);
break;
default:
break;
}
}
SeqListDestroy(&mylist);
printf("GoodBye.\n");
return 0;
}
**seqlist.h**
#ifndef _SEQ_LIST_H_
#define _SEQ_LIST_H_
#include"Common.h"
#define INC_SIZE 3
typedef struct SeqList
{
ElemType *base;
size_t capacity;
size_t size;
}SeqList;
bool _Inc(SeqList *pst);
bool _IsFull(SeqList *pst);
bool _IsEmpty(SeqList *pst);
void SeqListInit(SeqList *pst, size_t capacity);
void SeqListPushBack(SeqList *pst, ElemType v);
void SeqListPushFront(SeqList *pst, ElemType v);
void SeqListPopBack(SeqList *pst);
void SeqListPopFront(SeqList *pst);
void SeqListEraseByPos(SeqList *pst, int pos);
void SeqListEraseByVal(SeqList *pst, ElemType key);
void SeqListInsertByPos(SeqList *pst, int pos, ElemType v);
void SeqListInsertByVal(SeqList *pst, ElemType v);
size_t SeqListCapacity(SeqList *pst);
size_t SeqListLength(SeqList *pst);
ElemType SeqListFindByPos(SeqList *pst, int pos);
int SeqListFindByVal(SeqList *pst, ElemType key);
void SeqListSort(SeqList *pst);
void SeqListReverse(SeqList *pst);
void SeqListClear(SeqList *pst);
void SeqListDestroy(SeqList *pst);
void SeqListShow(SeqList *pst);
bool _Inc(SeqList *pst)
{
pst->base = (ElemType *)realloc(pst->base, sizeof(ElemType) * (pst->capacity+INC_SIZE));
if(pst->base == NULL)
return false;
pst->capacity += INC_SIZE;
return true;
}
bool _IsFull(SeqList *pst)
{return pst->size >= pst->capacity;}
bool _IsEmpty(SeqList *pst)
{return pst->size == 0;}
void SeqListInit(SeqList *pst, size_t capacity)
{
pst->capacity = capacity;
pst->base = (ElemType*)malloc(sizeof(ElemType) * pst->capacity);
assert(pst->base != NULL);
memset(pst->base, 0, sizeof(ElemType)*pst->capacity);
pst->size = 0;
}
void SeqListPushBack(SeqList *pst, ElemType v)
{
if(_IsFull(pst) && !_Inc(pst))
{
printf("顺序表容量不足,%d 不能插入.\n", v);
return;
}
pst->base[pst->size++] = v;
}
void SeqListPushFront(SeqList *pst, ElemType v)
{
if(_IsFull(pst) && !_Inc(pst))
{
printf("顺序表容量不足,%d 不能插入.\n", v);
return;
}
for(int i=pst->size; i>0; --i)
{
pst->base[i] = pst->base[i-1];
}
pst->base[0] = v;
pst->size++;
}
void SeqListPopBack(SeqList *pst)
{
if(_IsEmpty(pst))
{
printf("顺序表已空,不能删除.\n");
return;
}
pst->size--;
}
void SeqListPopFront(SeqList *pst)
{
if(_IsEmpty(pst))
{
printf("顺序表已空,不能删除.\n");
return;
}
for(int i=0; i<pst->size-1; ++i)
pst->base[i] = pst->base[i+1];
pst->size--;
}
void SeqListInsertByPos(SeqList *pst, int pos, ElemType v)
{
if(_IsFull(pst) && !_Inc(pst))
{
printf("顺序表容量不足,%d 不能插入.\n", v);
return;
}
if(pos<0 || pos>pst->size)
{
printf("插入的位置非法, %d 不能插入.\n", v);
return;
}
for(int i=pst->size; i>pos; --i)
pst->base[i] = pst->base[i-1];
pst->base[pos] = v;
pst->size++;
}
void SeqListInsertByVal(SeqList *pst, ElemType v)
{
int pos = 0;
while(pos<pst->size && v>pst->base[pos])
pos++;
SeqListInsertByPos(pst, pos, v);
}
void SeqListEraseByPos(SeqList *pst, int pos)
{
if(pos<0 || pos>=pst->size)
{
printf("删除的位置非法,不能删除数据.\n");
return;
}
for(int i=pos; i<pst->size-1; ++i)
pst->base[i] = pst->base[i+1];
pst->size--;
}
void SeqListEraseByVal(SeqList *pst, ElemType key)
{
int pos = SeqListFindByVal(pst, key);
if(pos == -1)
{
printf("要删除的数据不存在.\n");
return;
}
SeqListEraseByPos(pst, pos);
}
ElemType SeqListFindByPos(SeqList *pst, int pos)
{
assert(pos>=0 && pos<pst->size);
return pst->base[pos];
}
int SeqListFindByVal(SeqList *pst, ElemType key)
{
for(int i=0; i<pst->size; ++i)
{
if(key == pst->base[i])
return i;
}
return -1;
}
size_t SeqListLength(SeqList *pst)
{
return pst->size;
}
size_t SeqListCapacity(SeqList *pst)
{
return pst->capacity;
}
void SeqListClear(SeqList *pst)
{
pst->size = 0;
}
void SeqListSort(SeqList *pst)
{
for(int i=0; i<pst->size-1; ++i)
{
bool is_swap = false;
for(int j=0; j<pst->size-i-1; ++j)
{
if(pst->base[j] > pst->base[j+1])
{
Swap(&(pst->base[j]), &(pst->base[j+1]));
is_swap = true;
}
}
if(!is_swap)
break;
}
}
void SeqListReverse(SeqList *pst)
{
int left = 0;
int right = pst->size-1;
while(left < right)
{
Swap(&(pst->base[left]), &(pst->base[right]));
left++;
right--;
}
}
void SeqListDestroy(SeqList *pst)
{
free(pst->base);
pst->base = NULL;
pst->capacity = pst->size = 0;
}
void SeqListShow(SeqList *pst)
{
for(int i=0; i<pst->size; ++i)
printf("%d ", pst->base[i]);
printf("\n");
}
#endif ;
**common.h**
#ifndef _COMMON_H_
#define _COMMON_H_
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<vld.h>
#define ElemType int
void Swap(ElemType *a, ElemType *b)
{
ElemType tmp = *a;
*a = *b;
*b = tmp;
}
#endif ;
其他问题
内存泄漏检测工具需要将安装vld插件 库函数为:#include<vld.h>
#ifndef +名称与#endif+名称是预防头文件的重复引入的操作。 用#pragma once也可以。 其实“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。比如:存在a.h文件#include "c.h"而此时b.cpp文件导入了#include “a.h” 和#include "c.h"此时就会造成c.h重复引用。 头文件被重复引用引起的后果: 有些头文件重复引用只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些,但是对于大工程而言编译效率低下那将是一件多么痛苦的事情。有些头文件重复包含,会引起错误,比如在头文件中定义了全局变量(虽然这种方式不被推荐,但确实是C规范允许的)这种会引起重复定义。
参考博客
https://blog.csdn.net/qq_44075108/article/details/108837950
|