数据结构概念

数据结构有三个要素:

  • 逻辑结构

  • 物理结构

  • 数据的运算

逻辑结构是指数据元素之间的逻辑关系。

  • 更贴近于现实,即从逻辑关系上来描述数据。

  • 是独立于计算机的,与计算机内部如何存储是无关的。

线性结构:线性表,栈,队列,双队列,串

非线性结构:集合、高维数组,树形结构、图状结构

物理结构:是指数据结构在计算机内的表示,也称为存储结构

线性表与链表

线性表:是n个具有相同特性的数据元素的有限序列。

线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都有且只有一个前趋元素,有且只有一个后继元素。

顺序存储结构的线性表,也叫顺序表。

链式存储结构的线性表,也叫链表。

链表有头结点。

链表

单链表

链表结点

struct Node{
  DataType val;    //DataType:数据类型 val: 数据的值
  int next;     //下一个结点的地址
};

内存管理

Node node[N];    //内存池,MAXN:最大结点数量
int p = 1;    //内存指针,指向等待被分配的结点

分配结点

int np = p++;
node[np]即为被分配的节点

头结点

头结点:该结点无数据,其next指向链表的第一个结点

int head = p++; //申请一个结点,该结点地址为head
node[head] 使用头结点

尾指针

尾指针:指向链表的最后一个结点

int tail = head; //初始情况下尾指针指向头结点

结点地址

结点地址(结点指针):结点在node数组中的下标

取结点地址k指向的结点:node[k]

尾插法添加结点

int np = p++; //申请一个结点 地址为np
node[np].val = 8;
node[tail].next = np;
tail = np;

头插法添加结点

int np = p++;
node[np].val = 8;
node[np].next = node[head].next;
node[head].next = np;

遍历链表

for(int i = node[head].next; i != 0; i = node[i].next){
    //...
    //此时取到结点为node[i]
}

链表插入(在k指向的结点后插入)

int np = p++; 
node[np].next = node[k].next;
node[k].next = np;

链表删除(删除k指向的结点的下一个结点)

int nk = node[k].next;
node[k].next = node[nk].next;

双链表

双向静态链表

struct Node {
     DataType val;//DataType:数据类型
    int pre; //pre:前一个结点的地址 
    int next; //next:下一个结点的地址
};

尾插法添加结点

int np = p++; //申请一个结点 地址为np
node[np].val = d;
node[tail].next = np;
node[np].pre = tail;
tail = np;

链表插入(在k指向的结点前插入,返回新结点地址)

int pk = node[k].pre;
int np = p++;
node[np].val = d;
node[np].next = k;
node[np].pre = pk;
node[k].pre = np;
node[pk].next = np;

头插法添加结点

int np = p++; 
node[np].val = d;
node[np].next = node[head].next;
node[np].pre = head;
node[node[head].next].pre = np;
node[head].next = np;

链表删除(删除k指向的结点)

int pk = node[k].pre;
int nk = node[k].next;
node[pk].next = nk;
node[nk].pre = pk;

线性表的两种存储结构顺序表与链表

C++ STL 双向链表

使用迭代器遍历list

list<int> lst;
//添加数据代码
for(list<int>::iterator it = lst.begin(); it != lst.end(); ++it) {
    cout << *it << endl;
}

要理解是栈,我们需要先举一个生活中的例子。

假如有一个又细又长的圆筒,圆筒一端封闭,另一端开口。往圆筒里放入乒乓球,先放入的靠近圆筒底部,后放入的靠近圆筒入口。

那么,要想取出这些乒乓球,则只能按照和放入顺序相反的顺序来取,先取出后放入的,再取出先放入的,而不可能把最里面最先放入的乒乓球优先取出。

栈(stack)是一种线性数据结构,它就像一个上图所示的放入乒乓球的圆筒容器,栈中的元素只能先入后出(First In Last out,简称FILO),最后进入的元素存放的位置叫作栈顶。

入栈操作(push)就是把新元素放入栈中,只允许从栈顶一侧放入元素,新元素的位置将会成为新的栈顶。

出栈操作( pop )就是把元素从栈中弹出,只有栈顶元素才允许出栈,出栈元素的前一个元素将会成为新的栈顶。

接下来,我们使用数组模拟栈,栈底指针不指向具体元素。

栈的逻辑结构

(stack):是一种受限定的线性表,限定其插入和删除操作只能在表的一端进行。

栈顶:允许操作的一端

栈底:不允许操作的一端

入栈 / 压栈 / 进栈(push)添加数据

出栈 / 弹栈 / 退栈(pop):删除数据

栈的特点:后进先出

表达式求值问题

中缀表达式:运算符位于操作数之间。

例:(3+4)×5-6

前缀表达式(波兰表达式):运算符位于操作数之前。

例:-×+3456

后缀表达式(逆波兰表达式):运算符位于操作数之后。

例:34+5×6-

例:求后缀表达式 34+5×6-的 值

求解后缀表达式

  1. 从左向右扫描字符串

  2. 遇到数字时,将数字入栈。

  3. 遇到运算符时,弹出栈顶的两个数,用该运算符对它们做相应的计算,结果入栈。

  4. 扫描结束后,栈顶数字就是结果。

带括号的中缀表达式转后缀

如果其他运算符在准备入栈时栈顶是左括号,运算符直接入栈。

如果右括号在准备入栈时,遇到栈顶是左括号,那么左括号出栈,这次入栈结束。

中缀:(1+2) * (6-5)

后缀:1 2 + 6 5 - *

例:求3+4×5-6的值

  1. 从左向右扫描表达式

  2. 遇到数字进数字栈

  3. 遇到运算符,如果运算符栈栈空,或该运算符优先级大于栈顶运算符优先级,则入栈。否则运算符出栈,从数字栈出栈2个数字,进行计算,结果在数字栈入栈。重复此过程。

  4. 扫描结束后,运算符栈出栈运算符,进行运算,直到运算符栈为空。最后数字栈栈顶的数字,就是结果。

队列

要理解什么是队列,我们同样可以用一个生活中的例子来说明。

假如公路上有一条单行隧道,所有通过隧道的车辆只允许通过隧道入口驶入,从隧道出口驶出,不允许逆行。这就是队列结构。

队列是限定在一端进行插入,另一端进行删除特殊线性表。就像排队买东西,排在前面的人买完东西后离开队伍(删除),而后来的人总是排在队伍未尾(插入)。通常把队列的删除和插入分别称为出队和入队。允许出队的一端称为队头,允许入队的一端称为队尾。所有需要进队的数据项,只能从队尾进入,队列中的数据项只能从队头离开。由于总是先入队的元素先出队(先排队的人先买完东西),这种表也称为先进先出(FIFO)表。

队列的数组实现

队列的链表实现

入队( enqueue )就是把新元素放入队列中,只允许在队尾的位置放入元素,新元素的下一个位置将会成为新的队尾。

出队操作( dequeue )就是把元素移出队列,只允许在队头一侧移出元素,出队元素的后一个元素将会成为新的队头。

队列的逻辑结构

队列(queue):是一种特殊的线性表,仅允许在一端插入,而在另一端删除。

队尾(tail):允许插入数据的一端

队头(head):允许删除数据的一端

入队(enqueue):插入数据

出队(dequeue):删除数据

队列的特点:先进先出

results matching ""

    No results matching ""