《算法导论》 — Chapter 10 基本数据结构
序
在本章中,要讨论如何通过使用了指针的简单数据结构表示动态集合。有很多的复杂的数据结构可以用指针来构造,本章介绍几种基本数据结构,包括栈、队列、链表,以及有根树。
栈
栈和队列都是动态集合,在这种结构中delete操作去掉的元素是预先规定好的。栈数据结构实现的是一种先进后出(FILO)的策略。作用于栈上的Insert操作称为压入Push,而无参数的Delete操作称为弹出Pop。
本章讨论的栈是用数组实现,数据结构定义为(CStack.h):
#ifndef _CSTACK_H
#define _CSTACK_H
#include <iostream>
class CStack{
public:
CStack();
~CStack();
int getTop()
{
return data[top];
}
bool empty()
{
return top == -1;
}
bool full()
{
return top == (maxSize - 1);
}
void push(int x);
void pop();
void display();
private:
int *data;
int top;
const int maxSize = 100;
};
#endif
如上所示,类CStack表示一个自定义栈,其包含数据成员data这样一个动态数组存储元素,top为当前栈顶元素的下标,当top==-1时表示栈空,当top==maxSize-1时表示栈满。
类实现(CStack.cpp)如下:
#include "CStack.h"
#include <iostream>
CStack::CStack()
{
top = -1;
data = new int[maxSize];
}
CStack::~CStack()
{
delete data;
}
void CStack::push(int x)
{
if (full())
{
std::cout << "栈已满!" << std::endl;
return;
}
else
top = top + 1;
data[top] = x;
}
void CStack::pop()
{
if (empty())
{
std::cout << "栈空!" << std::endl;
return;
}
else
std::cout << data[top--] << std::endl;
}
void CStack::display()
{
if (empty())
{
std::cout << "栈空!" << std::endl;
return;
}
for (int i = 0; i <= top; i++)
std::cout << data[i] << "\t";
std::cout << std::endl;
}
下面给出对以上自定义栈的测试程序(main.cpp)与测试结果:
#include "CStack.h"
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
CStack cs;
//显示栈中元素
cout << "当前栈中元素有:" << endl;
cs.display();
//添加元素入栈
for (int i = 0; i < 10; i++)
cs.push(i*i);
//显示栈中元素
cout << "当前栈中元素有:" << endl;
cs.display();
//显示栈顶元素
cout << "栈顶元素为:" << endl;
cout << cs.getTop() << endl;
//栈顶元素出栈
cout << "栈顶元素出栈:"<< endl;
cs.pop();
//重新打印栈元素
cout << "当前栈中元素有:" << endl;
cs.display();
system("pause");
return 0;
}
测试结果为:
队列
类似于数据结构栈,队列实现了一种先进先出(FIFO)的策略,在队列中,可以去掉的那个元素总是在集合中存在时间最长的那个。
我们把作用于队列上的Insert操作称为入对EnQueue,把作用于队列上的Delete操作称为出队DeQueue。
本章讨论的队列也是用数组实现的,基本结构(CQueue.h)如下:
#ifndef _CQUEUE_H_
#define _CQUEUE_H_
#include <iostream>
class CQueue{
public:
CQueue();
~CQueue();
bool empty()
{
return head == tail;
}
bool full()
{
return ((head == tail + 1) || (head == 0 && tail == maxSize-1));
}
void enQueue(int x);
void deQueue();
void display();
private:
int *data;
int head;
int tail;
//队列最大容量,最多存储maxSize-1个元素
const int maxSize = 10;
};
#endif
如上所示,队列使用数据成员data这样一个动态数组存储元素,另外还包含的队头,队尾两个成员变量,在本程序实现的是一个循环队列,当head == tail时队列为空,当((head == tail + 1) || (head == 0 && tail == maxSize-1))队列为满,整个队列中最多存储了maxSize-1个元素。
类实现(CQueue.cpp)如下:
#include "CQueue.h"
#include <iostream>
using namespace std;
CQueue::CQueue()
{
head = 0;
tail = 0;
data = new int[maxSize];
}
CQueue::~CQueue()
{
delete data;
}
void CQueue::enQueue(int x)
{
if (full())
{
cout << "队列已满" << endl;
return;
}
data[tail] = x;
if (tail == maxSize - 1)
tail = 0;
else tail += 1;
}
void CQueue::deQueue()
{
if (empty())
{
cout << "队列空!" << endl;
return;
}
cout << data[head] << endl;
if (head == maxSize - 1)
head = 0;
else
head += 1;
}
void CQueue::display()
{
if (empty())
{
cout << "队列空!" << endl;
return;
}
if (head > tail)
{
for (int i = head; i < maxSize-1; i++)
cout << data[i] << "\t";
for (int j = 0; j < tail; j++)
cout << data[j] << "\t";
}
else{
for (int i = head; i < tail; i++)
cout << data[i] << "\t";
}
cout << endl;
}
下面给出对以上自定义队列的测试程序(main.cpp)与测试结果:
#include "CQueue.h"
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
CQueue cq;
cout << "此时队的状态是:" << endl;
cq.display();
for (int i = 0; i < 9; i++)
cq.enQueue(i + 1);
//显示队列中所有元素
cout << "显示队中所有元素" << endl;
cq.display();
//再次入队一个元素
cout << "元素11入队!";
cq.enQueue(11);
//队头依次出队
cout << "队头元素:" << endl;
cq.deQueue();
cout << "队头元素:" << endl;
cq.deQueue();
cout << "队头元素:" << endl;
cq.deQueue();
//此时入队新元素
cout << "元素11入队!"<<endl;
cq.enQueue(11);
cout << "元素12入队!"<<endl;
cq.enQueue(12);
//再次显示队列中所有元素
cout << "显示队中所有元素" << endl;
cq.display();
system("pause");
return 0;
}
测试结果:
双向链表
在链表这种数据结构中,各个对象按照线性顺序排序。链表与数组不同,数组的线性顺序是由数组下标决定的,而链表中的顺序是由各个对象的指针决定的。
对于本节讨论的双向链表,定义结构(CList.h)如下:
#ifndef _CLIST_H_
#define _CLIST_H_
#include <iostream>
//双向链表结构体
typedef struct Node{
Node *prev;
Node *next;
int key;
Node(int x) :prev(NULL), next(NULL), key(x){}
};
class CList{
public:
CList();
~CList();
void Insert(int x);
Node *Search(int value);
void Delete(int x);
void Display();
private:
Node *head;
};
#endif
如上所示,结构体Node定义了链表中结点的基本内容,包括prev,next两个指针来实现双向性,以及一个key存储结点元素。在CList类中定义了4中基本操作,本处对于增、查、删操作函数参数均为基本元素而不是链表结点(书中是以结点作为参数的,本处认为以元素值作为参数封装性更好)。
类实现(CList.cpp)如下:
#include "CList.h"
#include <iostream>
#include <cstdlib>
using namespace std;
CList::CList()
{
head = NULL;
}
CList::~CList()
{
delete head;
}
void CList::Insert(int x)
{
//新建一个结点,其值为x
Node *node = new Node(x);
node->next = head;
if (head != NULL)
{
head->prev = node;
}
head = node;
head->prev = NULL;
}
Node * CList::Search(int value)
{
Node *node = head;
while (node != NULL && node->key != value)
{
node = node->next;
}
//如果找到相应结点
if (node)
return node;
else
return NULL;
}
//删除值为x的结点
void CList::Delete(int x)
{
if (head == NULL)
{
std::cout << "链表为空!" << endl;
return;
}
Node *node = Search(x);
if (node)
{
if (node->prev != NULL)
node->prev->next = node->next;
else
head = node->next;
if (node->next != NULL)
node->next->prev = node->prev;
delete node;
return;
}
else{
std::cout << "链表中无值为"<<x<<"的元素!" << endl;
return;
}
}
void CList::Display()
{
if (head == NULL)
{
std::cout << "链表为空!" << std::endl;
return;
}
Node *node = head;
while (node)
{
cout << node->key << "\t";
node = node->next;
}
cout << endl;
}
代码说明不再赘述,下面给出自定义双向链表的测试程序(main.cpp)以及测试结果:
#include "CList.h"
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
CList list;
//打印链表
list.Display();
//向链表插入元素
for (int i = 0; i < 10; i++)
list.Insert(i*i);
cout << "打印链表:" << endl;
list.Display();
//查找元素为9的结点
Node *node = list.Search(9);
cout << "查找元素为9的结点" << node->key << endl;
//删除元素为10的结点
list.Delete(10);
cout << "打印链表:" << endl;
list.Display();
//删除元素为36的结点
list.Delete(36);
cout << "打印链表:" << endl;
list.Display();
system("pause");
return 0;
}
测试结果:
带哨兵的循环双向链表
我们在处理边界条件时为了方便而且减少错误,经常使用哨兵(Sentinel),它是一个哑对象,可以简化边界条件。此处,在链表的实现中实现哨兵,不仅可以减少因边界处理不当而引起的指针异常,还可以实现循环的双向链表。基本操作与上类似,下面给出具体实现:
数据结构(SList.h)如下:
#ifndef _SLIST_H_
#define _SLIST_H_
#include <iostream>
/**
* 带哨兵的环形双向链表结构体
*/
typedef struct SNode{
SNode *prev;
SNode *next;
int key;
SNode(int x) :prev(NULL), next(NULL), key(x){}
};
class SList{
public:
SList();
~SList();
void Insert(int x);
SNode *Search(int value);
void Delete(int x);
void Display();
private:
SNode *nil;
};
#endif
类实现(SList.cpp)如下:
#include "SList.h"
#include <iostream>
using namespace std;
SList::SList()
{
nil = new SNode(0);
nil->prev = nil;
nil->next = nil;
}
SList::~SList()
{
delete nil;
}
void SList::Insert(int x)
{
//新建一个结点,其值为x
SNode *node = new SNode(x);
node->next = nil->next;
nil->next->prev = node;
nil->next = node;
node->prev = nil;
}
SNode * SList::Search(int value)
{
SNode *node = nil->next;
while (node != nil && node->key != value)
{
node = node->next;
}
//如果找到相应结点
if (node != nil)
return node;
else
return NULL;
}
//删除值为x的结点
void SList::Delete(int x)
{
if (nil->next == nil)
{
std::cout << "链表为空!" << endl;
return;
}
SNode *node = Search(x);
if (node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
delete node;
return;
}
else{
std::cout << "链表中无值为" << x << "的元素!" << endl;
return;
}
}
void SList::Display()
{
if (nil == nil)
{
std::cout << "链表为空!" << std::endl;
return;
}
SNode *node = nil->next;
while (node->next != nil)
{
cout << node->key << "\t";
node = node->next;
}
cout << endl;
}
下面给出测试程序(main.cpp)和测试结果:
#include "SList.h"
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
SList list;
//打印链表
list.Display();
//向链表插入元素
for (int i = 0; i < 10; i++)
list.Insert(i*i);
cout << "打印链表:" << endl;
list.Display();
//查找元素为9的结点
SNode *node = list.Search(9);
cout << "查找元素为9的结点" << node->key << endl;
//删除元素为10的结点
list.Delete(10);
cout << "打印链表:" << endl;
list.Display();
//删除元素为36的结点
list.Delete(36);
cout << "打印链表:" << endl;
list.Display();
system("pause");
return 0;
}
测试结果:
《算法导论》 — Chapter 10 基本数据结构的更多相关文章
- 算法导论——lec 10 图的基本算法及应用
搜索一个图是有序地沿着图的边訪问全部定点, 图的搜索算法能够使我们发现非常多图的结构信息, 图的搜索技术是图算法邻域的核心. 一. 图的两种计算机表示 1. 邻接表: 这样的方法表示稀疏图比較简洁紧凑 ...
- 《算法导论》 — Chapter 7 高速排序
序 高速排序(QuickSort)也是一种排序算法,对包括n个数组的输入数组.最坏情况执行时间为O(n^2). 尽管这个最坏情况执行时间比較差.可是高速排序一般是用于排序的最佳有用选择.这是由于其平均 ...
- 《算法导论》 — Chapter 7 快速排序
序 快速排序(QuickSort)也是一种排序算法,对包含n个数组的输入数组,最坏情况运行时间为O(n^2).虽然这个最坏情况运行时间比较差,但是快速排序通常是用于排序的最佳实用选择,这是因为其平均性 ...
- 数据结构和算法(Golang实现)(10)基础知识-算法复杂度主方法
算法复杂度主方法 有时候,我们要评估一个算法的复杂度,但是算法被分散为几个递归的子问题,这样评估起来很难,有一个数学公式可以很快地评估出来. 一.复杂度主方法 主方法,也可以叫主定理.对于那些用分治法 ...
- 基本数据结构(2)——算法导论(12)
1. 引言 这一篇博文主要介绍链表(linked list),指针和对象的实现,以及有根树的表示. 2. 链表(linked list) (1) 链表介绍 我们在上一篇中提过,栈与队 ...
- (搬运)《算法导论》习题解答 Chapter 22.1-1(入度和出度)
(搬运)<算法导论>习题解答 Chapter 22.1-1(入度和出度) 思路:遍历邻接列表即可; 伪代码: for u 属于 Vertex for v属于 Adj[u] outdegre ...
- 《算法导论》— Chapter 9 中位数和顺序统计学
序 在算法导论的第二部分主要探讨了排序和顺序统计学,第六章~第八章讨论了堆排序.快速排序以及三种线性排序算法.该部分的最后一个章节,将讨论顺序统计方面的知识. 在一个由n个元素组成的集合中,第i个顺序 ...
- 《算法导论》 — Chapter 8 线性时间排序
序 到目前为止,关于排序的问题,前面已经介绍了很多,从插入排序.合并排序.堆排序以及快速排序,每一种都有其适用的情况,在时间和空间复杂度上各有优势.它们都有一个相同的特点,以上所有排序的结果序列,各个 ...
- 《算法导论》— Chapter 15 动态规划
序 算法导论一书的第四部分-高级设计和分析技术从本章开始讨论,主要分析高效算法的三种重要技术:动态规划.贪心算法以及平摊分析三种. 首先,本章讨论动态规划,它是通过组合子问题的解而解决整个问题的,通常 ...
随机推荐
- Throwing Dice LightOJ - 1064 || (勉强能用的)分数类
Throwing Dice LightOJ - 1064 方法: 设ans[i][j]表示i个骰子点数恰好为j的概率.那么ans[1][1]到ans[1][6]都为1/6. 显然,$ans[i][j] ...
- 贪心 HDOJ 5090 Game with Pearls
题目传送门 /* 题意:给n, k,然后允许给某一个数加上k的正整数倍,当然可以不加, 问你是否可以把这n个数变成1,2,3,...,n, 可以就输出Jerry, 否则输出Tom. 贪心:保存可能变成 ...
- Kruskal HDOJ 1233 还是畅通工程
题目传送门 /* 最小生成树之kruskal算法--并查集(数据结构)实现 建立一个结构体,记录两点和它们的距离,依照距离升序排序 不连通就累加距离,即为最小生成树的长度 */ #include &l ...
- Harris角点检测原理及实现
一.原理 二.实现 close all; clear all; I=imread('test.tif'); [posX,posY]=harris(I); figure;imshow(I); hold ...
- SecureCRT的配色方法
配色后效果如下: 下面开始配色 1.首先配置成默认模式 2.终端(Terminal)==>仿真(Emulation) 按图中标注进行勾选,勾选完毕点击确认 2.选项(Options)==> ...
- [已读]基于MVC的Javascript Web 富应用开发
这本书是12年出版,我买的时间应该是13年,书架上唯一一本盗版→ → 但是看完是在今年. 因为刚拿到的时候,读起来很是磕磕绊绊,就搁置了蛮久.到第二次拿起来的时候,发现已经有部分内容过时,但我还是觉得 ...
- 生成HTML表格的后台模板代码
有时候,我们需要在后台拼接生成前端的html表格,一般的做法就是各种string.StringBuilder的拼接(例子省略...),这样的话如果表头不同就没法做到代码的重用,增加代码的冗余,下面我分 ...
- mac下安装nodejs
下载 https://nodejs.org/en/ 安装 一步步继续就ok 验证 npm -v node -v Done!
- ubuntu下安装apcu扩展
apcu前身是apc,apc分为系统缓存和用户缓存 1.系统缓存是指PHP执行时增加缓存,减少PHP文件的反复检查和编译,从而达到系统加速的目的. 2.用户缓存是指,PHP代码中将数据写入缓存,是用户 ...
- 多表单异步验证 可以用 Promise validate
https://www.jb51.net/article/140062.htm vue动态绑定组件子父组件多表单验证功能的实现代码 // 加载组件:每次下拉框监听则给changValue新赋值,如果下 ...