本系列课程主要介绍数据结构的一些知识,其实网上也有很多视频和详细的教程,笔者最近想找一些算法岗位,发现很多公司笔都是考字符串算法操作、数据结构(线性表、栈、队列、图,树)、动态规划、贪心算法、高级数据结构(字典树、线段树)等。
1.1 什么是线性表
从逻辑上来看,线性表是有n(n>=0)个数据元素组成的有序序列,线性表中元素的个数n(n>=0)定义为线性表的长度,n=0时称为空表。
对于一个非空的线性表,其机构特征如下:
(1)有且仅有一个开始结点,该结点没有直接前驱结点;
(2)有且仅有一个终结结点,该结点没有直接的后续结点;
(3)除(1)(2)外,其余内部结点都有且仅有一个直接前驱结点和一个直接后继结点;
对于同一线性表,各数据元素必须具有相同的数据类型。
1.2 线性表的操作
(1)InitList(L) 初始化操作,建立一个空的线性表;
(2)IsEmpty(L) 判断线性表是否为空,如果线性表为空,则返回true,如果线性表不为空,则返回false;
(3)ClearList(L) 将线性表中所有元素删除;
(4)GetElement(L,i) 返回线性表中下标为i的元素;
(5)LocateElement(L,e) 在线性表中查找指定元素e,若查找成功,返回元素e的下标,若查找不成功,返回-1;
(6)InsertList(L,i,e) 在线性表中下标为i的位置插入元素e;
(7)DeleteList(L,i) 将线性表中下标为i的元素删除;
(8)ListLength(L) 返回线性表中元素个数;
(9)ListTranverse(L,void(*func)) 访问线性表中所有元素;
1.3 线性表的顺序存储结构
顺序存储结构特点:(1)用一段地址连续的存储空间;(2)依次存储线性表中的数据元素。
数据长度和线性表长度的区别:数据长度是线性表中能保存的数据元素的最多个数,是一个固定值;线性表长度是线性表中已经保存的数据元素的个数,是一个随时变化的值。
顺序表地址计算方法:L(a(i)) = L(a(i-1)) + n = L(a(0)) + n*(i-1),其中i,i-1,0代表下标,n表示每个元素所占的字节数。
1.4 代码实现
(1)首先设置MAXSIZE为最大存储的数据长度。
typedef int DataType这一句如果不写的话,我们必须在结构体中指定数据类型,这样写的好处就是如果下次我们想把int修改为double或者float等类型是,我们就不需要去修改结构体里面的定义,只需要修改typedef就可以了。
结构体前面加一个typedef主要是方便后续的调用,如果不加每次定义就要写struct SeqList* L,加上typedef之后,每次定义只写SeqList* L。
#define MAXSIZE 100
typedef int DataType;
typedef struct SeqList
{
DataType data[MAXSIZE];
int length;
}SeqList;
(2)初始化,初始化只需将length设置为0。
void InitList(SeqList* list){
list->length = 0;
}
(3)判断线性表是否为空
bool IsEmpty(SeqList* list){
return list->length == 0;
}
(4)清除线性表
void ClearList(SeqList* list){
list->length = 0;
}
(5)返回元素个数
int ListLength(SeqList* list){
return list->length;
}
(6)返回指定元素,是否有人会问这里为什么会返回指针呢?直接返回int类型不行吗?如果这样DataType GetElement(SeqList* list,int i),那么如果查找的下标越界,我们返回什么呢?返回-1,假设原数据中存储的有-1这个值,此时返回的-1是查找正确还是下标越界的返回呢?这里就已经无法区分了,所有这里必须返回DataType*,然后根据指针操作可以取对应的值。
DataType* GetElement(SeqList* list, int i){
if (i<0 || i>(list->length-1)){
return NULL;
}
return &list->data[i];
}
(7)查找指定元素,如果找到则返回该元素下标,如果没有找到则返回-1。
int LocateElement(SeqList* list , DataType x){
for (int i = 0; i < (list->length); i++)
{
if (list->data[i] == x){
return i;
}
}
return -1;
}
(8)在指定位置插入一个元素
如果线性表长度已经是MAXSIZE,说明空间已经满,无法插入;
如果插入位置小于0,则插入到开头;如果插入位置大于线性表长度,则插入到数据最后;
每次插入成功,必须将线性表长度+1,这个操作会被忽略掉,一定注意。
bool InsertList(SeqList* list, int i, DataType x){
if (list->length == MAXSIZE)
{
return false;
}
int pos = 0;
//开头
if (i < 0)
{
pos = 0;
}
//最后
if (i > list->length-1){
pos = list->length;
}
for (int j = list->length-1; j>=pos; j--)
{
list->data[j + 1] = list->data[j];
}
list->data[pos] = x;
list->length++; //插入完成,线性表数据元素+1
return true;
}
(9)删除一个元素
如果删除的下标不存在,则返回false;
如果找到要删除的下标,把下标后面的所有元素都前移一个位置;
删除成功后,线性表长度-1,这个操作会被忽略掉,一定注意。
bool DeleteList(SeqList* list, int i){
if (i < 0 || i >= (list->length))
{
return false;
}
for (int j = i; j < list->length; j++)
{
list->data[j] = list->data[j + 1];
}
list->length--;
return true;
}
(10)合并两个线性表
第一个if判断可能有些问题,这里要分情况讨论,如果只是合并不做相同元素排除的话,两个顺序表的长度之和不能大于最大值,如果相同元素不合并,则需要先判断排除相同元素后,两个表的长度是否大于最大值,这里不是很全,如果用到的同学可以自己补充一下逻辑判断。
void UnionList(SeqList* a, SeqList*b){
if ((a->length + b->length) > MAXSIZE)
{
return;
}
for (int i = 0; i < b->length; i++)
{
DataType e = b->data[i];
if (LocateElement(b,e) == -1)
{
InsertList(a, a->length, e);
}
}
}
(11)顺序表倒置
这里不用过多介绍,找到中间位置,第一个和最后一个交换即可实现。
void reverse(SeqList* list){
if (list->length<0)
{
return;
}
for (int i = 0; i < list->length / 2; i++)
{
DataType e = list->data[i];
list->data[i] = list->data[list->length - 1 - i];
list->data[list->length - 1 - i] = e;
}
}
(12)顺序表遍历
ListTranverse方法第二个参数是一个函数指针,调用时只需要传递一个函数即可。
void Print(DataType e){
cout << "e=" << e << endl;
}
void ListTranverse(SeqList* list, void(*printFun)(DataType) ){
for (int i = 0; i < list->length;i++)
{
printFun(list->data[i]);
}
}
(13)main方法中调用
这里只写了简单的调用,如果有需要可以完善main函数,把所有函数都可以调用一遍。
int main(){
SeqList sl;
InitList(&sl);
InsertList(&sl, 0, 10);
DataType* node = GetElement(&sl, 0);
cout << "GetElement==" << *node << endl;
ListTranverse(&sl, Print);
system("pause");
return 0;
}
笔者不是写C和C++的,只是学了一些然后开始写小的算法程序,如果有写的不正确的地方还请大神们指点。
最终完整的代码如下:
#include <stdio.h>
#include <iostream>
using namespace std;
#define MAXSIZE 100
typedef int DataType;
typedef struct SeqList
{
DataType data[MAXSIZE];
int length;
}SeqList;
//初始化线性表
void InitList(SeqList* list){
list->length = 0;
}
//判断线性表是否为空
bool IsEmpty(SeqList* list){
return list->length == 0;
}
//清除线性表
void ClearList(SeqList* list){
list->length = 0;
}
//返回数据元素个数
int ListLength(SeqList* list){
return list->length;
}
//获取指定元素
DataType* GetElement(SeqList* list, int i){
if (i<0 || i>(list->length-1)){
return NULL;
}
return &list->data[i];
}
//在线性表中查找指定的元素
int LocateElement(SeqList* list , DataType x){
for (int i = 0; i < (list->length); i++)
{
if (list->data[i] == x){
return i;
}
}
return -1;
}
//在线性表指定的位置插入一个元素
bool InsertList(SeqList* list, int i, DataType x){
if (list->length == MAXSIZE)
{
return false;
}
int pos = 0;
//开头
if (i < 0)
{
pos = 0;
}
//最后
if (i > list->length-1){
pos = list->length;
}
for (int j = list->length-1; j>=pos; j--)
{
list->data[j + 1] = list->data[j];
}
list->data[pos] = x;
list->length++; //插入完成,线性表数据元素+1
return true;
}
bool DeleteList(SeqList* list, int i){
if (i < 0 || i >= (list->length))
{
return false;
}
for (int j = i; j < list->length; j++)
{
list->data[j] = list->data[j + 1];
}
list->length--;
return true;
}
//合并两个线性表
void UnionList(SeqList* a, SeqList*b){
if ((a->length + b->length) > MAXSIZE)
{
return;
}
for (int i = 0; i < b->length; i++)
{
DataType e = b->data[i];
if (LocateElement(b,e) == -1)
{
InsertList(a, a->length, e);
}
}
}
//线性表倒置
void reverse(SeqList* list){
if (list->length<0)
{
return;
}
for (int i = 0; i < list->length / 2; i++)
{
DataType e = list->data[i];
list->data[i] = list->data[list->length - 1 - i];
list->data[list->length - 1 - i] = e;
}
}
//线性表的遍历
void Print(DataType e){
cout << "e=" << e << endl;
}
void ListTranverse(SeqList* list, void(*printFun)(DataType) ){
for (int i = 0; i < list->length;i++)
{
printFun(list->data[i]);
}
}
int main(){
SeqList sl;
InitList(&sl);
InsertList(&sl, 0, 10);
DataType* elem = GetElement(&sl, 0);
cout << "GetElement==" << *elem << endl;
ListTranverse(&sl, Print);
system("pause");
return 0;
}