赞
踩
链表实际上是线性表的链式存储结构,与数组不同的是,它是用一组任意的存储单元来存储线性表中的数据,存储单元不一定是连续的,
且链表的长度不是固定的,链表数据的这一特点使其可以非常的方便地实现节点的插入和删除操作
单向链表很容易理解,就说把指针当成一条链一样连接每一个节点,每个节点包含了一个或者多个数据。链表的存储单元不一定是连续的,长度不固定,主要用于方便实现节点的插入和删除操作
。官方的话这里就不说了,作为数据结构里最基本的,感兴趣的童鞋自行看书
//单向链表节点结构
typedef struct dlink_node
{
struct dlink_node *next;
void *val; //能存储任意类型数据
}node;
在单向链表的基础上,增加了一个指向前一个节点的指针
//双向链表节点结构
typedef struct dlink_node
{
struct dlink_node *prev;
struct dlink_node *next;
void *val; //能存储任意类型数据
}node;
链表的两头连接,形成了一个环状链表,称为循环链表。
循环链表与单链表的主要差异在于循环的判断上,原来是判断p->next是否为空,现在则是p->next不等于头结点,则循环未结束。
判断循环链表为空: front=rear
队满时: (rear+1)%maxsize=front (rear的下一个是front)
front指向队首元素,rear指向队尾元素的下一个元素。
循环链表还可以引申出环形链表(常用于判断链表是否有环)
链表的构造:
struct ListNode
{
double value;
ListNode *next;
// ListNode *pre; 双向链表
};
创建一个新链表节点:
ListNode *head = new ListNode(0);创建一个新节点并赋值为0
访问下一个节点和值
head = head->next;
cout<<head->val;
单独写在另一篇文章:
先进先出(FIFO)——先进队列的元素先出队列。来源于我们生活中的队列(先排队的先办完事)。
typedef struct queue
{
int queuesize; //数组的大小
int head, tail; //队列的头和尾下标
int *q; //数组头指针
}Queue;
线性表有顺序存储和链式存储,队列作为一种特殊的线性表,也同样存在这两种存储方式。
用数组存储队列,即利用一组地址连续的存储单元依次存放队列中的元素。为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针(头尾指针):front指针指向队头元素,rear指针指向队尾元素的下一个位置。初始化时的头尾指针,初始值均为0。入队时尾指针rear加1,出队时头指针front加1,头尾指针相等时队列为空。
这里可以看到,队尾一般指向已队列元素的前一个位置
如何解决“溢出”问题呢:循环队列。
顺序队列的 “假溢出” 问题:队列的存储空间未满,却发生了溢出。比如尾指针现在虽然已经指向了最后一个位置的下一位置,但是之前队头也删除了一些元素,那么头指针经历若干次的加1之后,留出了很多空位置,但是顺序队列还在傻乎乎的以为再有元素入队就溢出呢!肯定不合理。故循环队列诞生!
所以解决"假溢出"的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。将新元素插入到第一个位置上,入队和出队仍按先进先出的原则进行,操作效率高,空间利用率高。
虽然使用循环队列,解决了假溢出问题,但是又有新问题发生——判空的问题,因为仅凭 front = rear 不能判定循环队列是空还是满。比如如图:
解决办法:
我们重点来讨论第二种方法,由于rear可能比front大,也可能比front小,所以尽管它们只相差一个位置时就是满的情况,但也可能是相差整整一圈。 所以若队列的最大尺寸为QueueSize,那么队列满的条件是(rear+1) % QueueSize == front
。通用的计算队列长度公式为:(rear - front + QueueSize) % QueueSize
。
注意:front指针和rear指针后移不能直接使用++,而要使用%:
Q->front = (Q->front + 1) % MAXSIZE,因为到达数组尾后需要移动到数组开头。
队列的链式存储结构,其实就是一个操作受限的单向链表,只不过它只能尾进头出而已,我们把它简称为链队列。链式队列和单向链表比就多了两个指针,头指针和尾指针。
优点:
缺点:
参考:https://zhuanlan.zhihu.com/p/75950769
C++队列queue模板类的定义在头文件中,queue 模板类需要两个模板参数,一个是元素类型,一个容器类型,元素类型是必要的,容器类型是可选的,默认为deque 类型。C++队列Queue是一种容器适配器,它给予程序员一种先进先出(FIFO)的数据结构。
头文件:#include
那么我们如何判断队列是空队列还是已满呢?
非循环队列:
循环队列:
创建一个队列
queue< int > q;
基本操作:
q.empty() 如果队列为空返回true,否则返回false
q.size() 返回队列中元素的个数
q.pop() 删除队列首元素但不返回其值
q.front() 返回队首元素的值,但不删除该元素
q.push() 在队尾压入新元素
q.back() 返回队列尾元素的值,但不删除该元素
q.swap(p); 将当前 queue 中的元素和参数 queue 中的元素交换。
#include <queue> #include <iostream> using namespace std; int main(){ queue<int> q; for (int i = 0; i < 10; i++){ q.push(i); } if (!q.empty()){ cout << "队列q非空!" << endl; cout << "q中有" << q.size() << "个元素" << endl; } cout << "队头元素为:" << q.front() << endl; cout << "队尾元素为:" << q.back() << endl; for (int j = 0; j < 10; j++){ int tmp = q.front(); cout << tmp << " "; q.pop(); } cout << endl; if (!q.empty()){ cout << "队列非空!" << endl; } system("pause"); return 0; }
队列q非空!
q中有10个元素
队头元素为:0
队尾元素为:9
0 1 2 3 4 5 6 7 8 9
#include <iostream> using namespace std; #define MAX 5 //循环队列 struct Queue{ int data[MAX]; int front;//队首指针 int rear;//队尾指针 }; //初始化队列 void init(Queue &q){ q.front = q.rear = 0; } //判断是否空 int isEmpty(Queue q){ return q.front==q.rear;//0不是空 } //判断是否满 int isFull(Queue q){ return (q.rear+1)%MAX == q.front;//0不是满 } //入队 bool enQueue(Queue &q, int val){ if(isFull(q)){//牺牲掉一个空间 cout<<"队满---\n"; return false; } q.data[q.rear] = val; q.rear = (q.rear+1)%MAX; return true; } //出队 bool deQueue(Queue &q){ if(isEmpty(q)){//判断是否为空 W cout<<"队列为空---"; return false; } q.front = (q.front+1)%MAX; return true; } //队头 int frontQueue(Queue q){ if(isEmpty(q)){ cout<<"队列为空"<<endl; return -1; } else return q.data[q.front]; } //队尾 void backQueue(Queue q){ if(isEmpty(q)){ cout<<"队列为空"<<endl; } else{ cout<<"队尾一般指向已队列元素的后一个位置,所以一般不会去读取循环队列的队尾元素"<<endl; } } //队列长度 int queueSize(Queue q){ if(q.front==q.rear) return 0; else return (q.rear - q.front + MAX) % MAX; } int main(){ Queue q; init(q); /* 牺牲掉一个空间判断是否队满 所以元素最多是 MAX-1个 */ for(int i=0; i<5; i++){ enQueue(q,i); } cout<<"队列是否为空: "<<isEmpty(q)<<endl; cout<<"队列是否满了: "<<isFull(q)<<endl; cout<<"队列的元素长度是:"<<queueSize(q)<<endl; cout<<"队头的值:"<<frontQueue(q)<<endl; backQueue(q); if(!deQueue(q)) cout<<"出队失败"<<endl; cout<<"出队后队头的值:"<<frontQueue(q)<<endl; cout<<"入队3元素"<<endl; if(!enQueue(q,3)) cout<<"入队失败"<<endl; return 0; }
队满---
队列是否为空: 0
队列是否满了: 1
队列的元素长度是:4
队头的值:0
队尾一般指向已队列元素的后一个位置,所以一般不会去读取循环队列的队尾元素
出队后队头的值:1
入队3元素
leetcode查看
参考:https://www.cnblogs.com/QG-whz/p/5170418.html#_label0
特点:
压栈:栈的插入操作,叫做进栈,也称压栈、入栈。
弹栈:栈的删除操作,也叫做出栈。
常用操作:
s.empty(); //如果栈为空则返回true, 否则返回false;
s.size(); //返回栈中元素的个数
s.top(); //返回栈顶元素, 但不删除该元素
s.pop(); //弹出栈顶元素, 但不返回其值
s.push(); //将元素压入栈顶
基于数组的栈
#include <stack> #include <iostream> using namespace std; int main() { stack<int> mystack; int sum = 0; for (int i = 0; i <= 10; i++){ mystack.push(i); } cout << "size is " << mystack.size() << endl; while (!mystack.empty()){ cout << " " << mystack.top(); mystack.pop(); } cout << endl; cout << "size is " << mystack.size() << endl; system("pause"); return 0; } //size is 11 // 10 9 8 7 6 5 4 3 2 1 0 //size is 0
基于单链表的栈
参考:https://blog.csdn.net/zichen_ziqi/article/details/80807989
leetcode查看
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。