数据结构简单总结(上)

  • 线性表
  1. 顺序线性表的数据结构

大概就是

Struct SqList{

ElemType* elem;//这个是本体

Int iLength;//长度

};

因为是指针形式的,所以一定要记得改指针的值;

顺序表也有几个重要的算法,比如插入,删除。取下标为i的值

顺序表的优点是可以随机存取,缺点是插入删除的时间复杂度大。

插入的时候,在插入位置后面的元素都要等于前面的元素,而且是从后往前。

删除则相反。从前往后删。

同时记得iLength--;

  1. 链表的数据结构和算法

数据结构

Struct Lnode{

ElemType data;

Lnode* next;//指向下一个结点

}*LinkList;

一定要注意递归调用。Ptr改成next

链表分为带头结点的和不带头结点的

带头结点的,顾名思义,就是LinkList->next,此时指向的结点才是首元结点;

不带头结点,那么LinkList这个头结点本身指向的就是首元结点。

一般,带头结点的初始化就是

LinkList=new Lnode;

LinkList->next=nullptr;//先将首元结点清空为null;

要注意清空和摧毁链表的区别

清空是保留头结点,要用两个变量,一个赋值首元结点,一个赋值成首元结点,一个是赋值成正在delete的那个结点。

取链表中第i个元素

因为链表不能进行随机存取,所以第取i个元素,只能从头开始遍历。

删除插入第i个元素

同理,与上面一样,先要从头开始遍历,一直遍历到第i-1个元素。

注意他是怎么遍历的!

先Lnode* p=LinkList->next;//先让他等于首元结点

j=0;

然后while(p && j<i-1){p=p->next;j++;}//这是遍历到第i-1个元素

然后下面进行判断

If(!p || j>i) !p是说明遍历到头了 j>i说明给的数是负数!

插入的时候,假设p是待插入的结点

  1. >next=LinkList->next;

LinkList->next=p;//这样即可实现插入了;

删除的话,也是先进性遍历,遍历到指定位置的前一个。用一个元素存一下

P=LinkList->next;

LinkList->next=p->next;

Delete p;这样即可

接下来就是头插法和尾插法

头插法不需要额外增加新的变量,因为头是固定的,而尾插法需要增加一个Lnode* 的变量,用来存储尾结点。

头插法就是P->next=L->next;L->next=p;

尾插法先弄个尾巴变量r r->next=p;p->next=nullptr;r=p;//修改尾巴变量;

  1. 双向链表、循环链表

循环列表,就是接着的

可以从任意一个结点出发找到其他结点,没必要非得从头结点出发了

所谓双向链表,就是有两个指针域,一个是next后继,一个是prior前驱元素。

这样插入删除就非常方便了,但是是一种利用空间换取时间的一种存储形式。

最后,总复习进行比较

所谓堆栈、队列

就是线性表,但是,是有一定限制的线性表。

栈是FILO先进后出的线性表。即限制了他进出要先进栈的后出去。

而队列FIFO,限制了他先进要后出的结构。

  1. 栈的数据结构和算法

数据结构

因为是线性表,所以栈也有顺序栈和链栈

一般来说是顺序栈,链栈不多见。

数据结构

Struct SqStack{

ElemType* base;//栈底

ElemType* top;//栈顶

};

两个指针,一个存栈顶,一个是栈底;

初始化的时候

Base=new ElemType[xx];

Top=base,这样当他们相等的时候,就是为空了.

栈的两个重要操作就是push和pop操作

Push操作

Push,即入栈,首先进行判断,是不是已经到栈顶了,没有的话直接

*(top++)=e;e是待进栈的元素;

Pop操作

Pop就是出栈,首先进行判断,是不是已经栈空了,如果不是那么就直接出栈就好了

Top--;直接这样即可;

  1. 队列的数据结构和算法

队列也分为顺序队列和链队列;

一般来说使用顺序循环队列。

顺序循环队列就是下标首和尾是链接起来的;

还有很多定义,比如双向队列等等

单链队列的数据结构

Struct QueueNode{

ElemType* data;

QueueNode* next;

}*QueuePtr;//注意 这里和定义链表还是完全一样的

Struct QueueLink{

QueuePtr* front;//对头

QueuePtr* rear;//队尾;

}

循环顺序链表的数据结构

Struct SqQueue{

ElemType* data;

Int front;

Int rear;

}

注意,循环列表区别队满和队空的方法很多,我们这里用空出一个元素来表示。

rear==front,代表空,(rear+1)%QueueLength==front;代表队满;

顺序队列的几个重要算法

进队列

先判断是不是队满,队满的判断参照上面如何判断的

然后Q.data[Q.rear]=e;

  1. rear=(Q.rear+1)%SIZE;

出队列

先判断是不是队空

Q.front=(Q.front+1)%SIZE;

初始化

初始化那就是先把data更新一下,地址是可读写的

然后把rear=front即可

总结一下,队列和栈都是线性表,只是限制了进出的顺序。

而且加了一些新的东西

比如栈,是直接两个指针

而队列,(顺序循环队列),则在顺序表的基础上,加了两个int变量rear和front

所谓字符串,其实也是线性表,简而言之,就是限定了存储类型的线性表。

比如字符串,限定必须储存char。

字符串最重要的两个算法。

串的模式匹配算法

BF和KMP算法

BF是传统的,暴力枚举,效率较低。

而KMP的时间效率高O(m+n)

下面简单的举出这两种算法

首先,字符串的数据结构

一般来说,T[0]是来存储字符串长度的

首先,暴力BF算法的字串匹配

算法思想就是从字串的第一个与主串的第一个进行比较

一个字符一个字符地进行比较,匹配指针指向下一个,不匹配那么就指向

一旦不匹配,那么子串指针都回退到0 主串的指针经过计算回退到某个位置(上次比较的下个位置)其实就是i=i-(j-1)+1=i-j+2;//这样就不用在另设个空间了

Int Sstring::Index_BF(Sstring s){//c++形式 this->T代表主字符串

//s是字串

Int i=1,j=1;

While(i<=s.ch[0] && j<=this->ch[0]){

If(s.ch[j]==this->ch[i]){i++;j++}

else{j=1; i=i-j+2; }

}

If(j>s.ch[0]) return i-t.ch[0]//用找到的i-字串长度 就是起始位置了

}

其次 难度较高的KMP算法的子串匹配

KMP算法的思想是主串的指针不回退,

那不会退的话会不会遗漏呢?

答案是且字串的指针回退也是经过Next[i]计算的

这样,子串巧妙的回退,就不会有遗漏了。

比如acabaabaabca

子串abaabca

先匹配

第一个可以

第二个不行,那么子串指针回退,回退到看看前缀的后缀有多少相等的(最大)

回退的位置是这个值+1;

如果没有,即0,那么就是1,也就是回退到0;

比如

acabaabaa...

  abaabc  这个时候出现不匹配的情况 要进行回退

而这个时候 子串的前缀ab 和后缀ab相等,那么就是要回退到第三个位置再进行比较

acabaabaa

(ab)aa....这样来进行比较

假定next[j]的数组已经被求出来 那么KMP的算法就是

Int Sstring::Index_KMP(Sstring s){//c++形式 this->T代表主字符串

//s是字串

Int i=1,j=1;

While(i<=s.ch[0] && j<=this->ch[0]){

If(j==0 || s.ch[j]==this->ch[i]){i++;j++}

else{j=next[j]; }//其他的都是很一样

}

If(j>s.ch[0]) return i-t.ch[0]//用找到的i-字串长度 就是起始位置了

}

至于next数组到底怎么求,这个比较难,

下面贴出算法

Void Get_next(){

I=1;

next[1]=0;

j=0;

While(i<this->ch[0]){

//小于长度

If(j==0 ||this->ch[i]==this->ch[j] ){i++;j++ next[i]=j;

Else{

J=next[j];

}

}

}

用的最广的就是二叉树、霍夫曼树

树是一种一对多的数据结构。之前学的都是一个元素

只有一个前驱或者后继元素。但是树可以有多个后继。1:n的数据结构

有几个后继就叫做几叉树;最常用的是二叉树。

二叉树相关概念和性质

孩子、双亲。双亲的指针域指向孩子。

满二叉树:就是深度为k 结点为2^k-1的二叉树

完全二叉树:就是满二叉树,按照逆顺序去掉结点。

二叉树的数据结构

同理,二叉树也有顺序的和链表的。

按照顺序储存的二叉树:

ElemType SqBiTree[SIZE];

直接这样即可,下标就是按照满二叉树(完全二叉树)来进行顺序排序。

如果是一般的二叉树,那么对照着满二叉树,再原有的空缺的下标填0;

按照链式存储的二叉树:

数据结构

Struct BiTNode{

ElemType data;

Struct BiTNode* lChild,rChild;

}*BiTree;

这个就是链表存储二叉树的数据结构

遍历二叉树

二叉树尤为重要,二叉树分为3中遍历方式

前序、中序、后序遍历。

这里用递归的调用是最简单的。

也可以采取非递归的,

比如用队列存储(层次遍历)

前序遍历

掌握前序遍历就可以掌握了其他两种遍历

Void PreOrderTraverse(BiTree T){

If(T){

//t不为空

Visit(T);//前序遍历,先访问头结点

PreOrderTraverse(T->lChild);//递归 想改变顺序 只需要将Visit(T)改变位置即可

PreOrderTraverse(BiTree->rChild);

}

}

层次遍历

用到了队列,不管什么前中后了

利用队列先进先出的性质,一层一层地进去。进队列,然后出队列访问,顺便把他左右孩子进队列。等到队列空的时候,那么久直接访问完成了;

Void LeverTraverse(BiTree T){

Queue Q;

  1. EnQue(T);

While(!Q.empty()){

auto e=Q.Deque;

Visit(e);

Q.enque(e.lChild);

Q.enque(e.rChild);//左右孩子进队列

}

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值