我为什么想要学数据结构?
在计算机界有这样一个万能公式:数据结构 + 算法 = 程序.
在如今这计算机引领风骚的时代,不学数据结构,你凭什么想要做时代的弄潮儿;所以我毅然决然的提前自学了数据结构。
学习数据结构前的我是这样认为的?!
什么是数据结构?数据结构要用什么语言实现?学了数据结构后能做什么?
学习数据结构后的我有什么感触?
数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。
记为:
Data_Structure=(D,R)
其中D是数据元素的集合,R是该集合中所有元素之间的关系的有限集合。
数据结构是一种处理数据的思想;
数据结构是一种处理数据的模型(计算机的本领就是处理数据的能力超强),可见数据结构的重要性!
学习技巧:
**实践是检验真理的唯一标准,也是学习数据结构的唯一技巧**
文中所有的代码实现都使用了类模板,大家使用时可酌情选择!
第一夜:线性表
线性结构:数据结构中的元素存在一对一的相互关系,
线性表是N个数据元素的有限序列;
以下主要总结了两种特例线性结构:顺序表和链表;
线性表之——顺序表篇
用基本数据类型—数组 去实现顺序表的增改删查的方法,
话不多说,直接上源码解释;
/*
代码要求实现效果:建立一个顺序表存取一下序列:
1,3,5,7,9
对于线性表中的每一个元素,在该元素前的一个元素叫 前驱 ,
在该元素后的一个元素叫 后继 ,
第一个元素只有 后继,最后一个元素只有 前驱;
*/
#ifndef LIST_H
#define LIST_H
/*
#ifndef LIST_H
#define LIST_H
...
#endif
这样一段话的意思是 如果没有定义头文件的别名,就定义它
他的作用是,如果有其他文件多次调用这个头文件,那么为了防止重复定义,加入
判断语句,只有第一次会进行定义,当第一个定义后,其他的调用定义会忽略。
*/
#include
using namespace std;
template <class Datatype> // 定义类模板
class List
{
public:
List(int size); //构造函数,定义数组大小为size
~List();
void clearList(); //清空该顺序表
bool listEmpty(); //用于判断该表是否为空,返回bool类型
int listLength(); //用于检测数组(表)中以存元素的数量
Datatype listGet(int i); //获取数组(表)中指定的索引的元素;
int listLocate(Datatype x); //用于获取指定元素在数组中第一次出现位置的索引;
Datatype Precursor(Datatype x); //获取指定元素的前驱;
Datatype next(Datatype x); //获取指定元素的后驱
bool listInsert(int i,Datatype x); //用于在索引为i的位置插入元素
Datatype listDelete(int i); //用于删除索引为i的元素;
void printList(); //遍历并打印出该顺序表;
private:
Datatype *p_List; //定义表的指针变量
int Size; // 用于初始化表的大小
int Length; //存表所存元素的数量
};
template <class Datatype>
List::List( int size )
{
Size = size; //有用户传入参数以定义表的大小;
Length = 0; //长度初始化为0;
p_List = new Datatype[Size]; //在堆中去申请一个数组用于实现顺序表(存数据);
}
template <class Datatype>
List::~List() //析构函数,用于整理申请的空间归仓;
{
delete []p_List;
p_List = NULL;
}
template <class Datatype>
void List::clearList()
{
Length = 0; // 将表的元素个数置 0
//(因为下面所有的操作都是基于元素长度来进行,
//所以这里将长度置为0就相当于在清空表)
}
template <class Datatype>
bool List::listEmpty()
{ //这里很容易理解吧!
if (Length == 0)
{
return true;
}
else
{
return false;
}
}
template <class Datatype>
int List::listLength()
{
return Length;
}
template <class Datatype>
Datatype List::listGet( int i )
{
//遍历整个表去寻找;
try
{
if (i>=0&&i<=Length)
{
return p_List[i];
}
else
{
throw 1; //所给元素越界,抛出错误!
}
}
catch(int) //捕捉错误;
{
cout<<"查找位置非法!"<template <class Datatype>
int List::listLocate( Datatype x )
{
for (int j=0;j//遍历整个数组寻找
{
if (x == p_List[j])
{
return j; //找到就返回元素的索引;
}
}
return 0; // 没找到就返回0;
}
template <class Datatype>
Datatype List::Precursor( Datatype x )
{
try
{
int temp = listLocate(x); //先定位到该元素;
//若有所指的元素有前驱便返回前驱;
if (temp!=0 && temp>0 && temp<=Length)
{
return p_List[temp-1];
}
else
{
throw 1;
}
}
catch(int)
{
cout<<"该位置没有前驱!"<template <class Datatype>
Datatype List::next( Datatype x )
{
try
{
//写法类似于找前驱!
int temp = listLocate(x);
if (temp>=0 && tempreturn p_List[temp+1];
}
else
{
throw 1;
}
}
catch (int)
{
cout<<"该位置没有后驱!"<//exit(1);
}
}
template <class Datatype>
bool List::listInsert( int i,Datatype x )
{
//插入元素时必须把插入点后面的元素都往后移动一个位置
if (i<0 || i>Length) //判断位置合法性;
{
return false;
}
for (int k=Length-1;k>=i;k--) //从后往前遍历,已达到移位的效果;
{
p_List[k+1] = p_List[k];
}
p_List[i] = x; //移动完毕,实施插入;
Length++; // 长度+1
return true; //返回插入成功的信号(bool类型)
}
template <class Datatype>
Datatype List::listDelete( int i)
{
//删除元素是,该元素之后的所有元素分别往前移动一位即可;
try
{
if (i<0||i>Length) //判断i的合法性;
{
throw 1;
}
Datatype temp;
temp = p_List[i]; //存下要删除的元素,以便于返回其值出来
for (int k=i;k//从后往前遍历,已达到移位的效果;
{
p_List[k] = p_List[k+1];
}
Length--;
return temp;
}
catch (int)
{
cout<<"删除位置非法!"<//exit(1);
}
}
template <class Datatype>
void List::printList()
{
//遍历输出表中所有元素;
for (int i=0;icout<< p_List[i] <#endif
#include "list.h"
#include
#include "Coordinate.h"
using namespace std;
int main()
{
cout<<list.listEmpty()<int num[5] = {1,3,5,7,9};
for (int i=0;i<5;i++)
{
list.listInsert(i,num[i]);
}
list.printList();
cout<< list.listLength()<int m,n;
m = list.listDelete(4);
n = list.listDelete(0);
cout<< m <cout<< n <list.listLength();
cout<<list.listGet(1)<cout<<list.Precursor(5)<cout<<list.next(3)<list.clearList();
cout<<list.listLength()<list.listDelete(1);
cout<<list.listLength()<list.Precursor(1);
list.printList();
system("pause");
return 0;
}
#ifndef COORDINATE_H
#define COORDINATE_H
#include
#include
using namespace std;
class Coordinate
{
//声明输出重载函数为友元函数;
friend ostream &operator<<(ostream &out,Coordinate &coor);
public:
Coordinate(int x=0,int y=0); //含默认参数的构造函数;
~Coordinate(){};
void printCoordinate(); // 打印出 点 ;
bool operator==(Coordinate &coor); //重载用算符 ==
protected:
int X;
int Y;
};
Coordinate::Coordinate( int x/*=0*/,int y/*=0*/ )
{
X = x;
Y = y;
}
void Coordinate::printCoordinate()
{
cout<< "(" << X << ',' << Y << ")" <operator<<(ostream &out,Coordinate &coor)
{
out<< "(" << coor.X << ',' << coor.Y << ")" <return out;
}
bool Coordinate::operator==( Coordinate &coor )
{
if (this->X == coor.X && this->Y == coor.Y)
{
return true;
}
else
{
return false;
}
}
#endif
以下为coordinate类的测试源码:
#include "list.h"
#include
#include "Coordinate.h"
using namespace std;
int main()
{
List list(5);
cout<<list.listEmpty()</*
int num[5] = {1,3,5,7,9};
for (int i=0;i<5;i++)
{
list.listInsert(i,num[i]);
}
list.printList();
cout<< list.listLength()<
Coordinate e1(1,1);
Coordinate e2(2,2);
Coordinate e3(3,3);
Coordinate e4(4,4);
list.listInsert(0,e1);
list.listInsert(1,e2);
list.listInsert(2,e3);
list.listInsert(3,e4);
list.printList();
cout<<list.listLocate(e3)<"pause");
return 0;
}
线性表之——链表篇
自我总结C++实现链表的思想在于用next指针把每一个节点类(结构体)串连起来,你可以想象一条链条…:
节点类的实现:
链表中每一个节点有两大特征:存数据的DATA + next指针;所以除了头结点外的每一个节点的地址都必须从上一个节点那里获取,也就是必须通过上一个节点的next来指向下一个节点,以此方法来对节点的访问;另外还有一点需要特别注意的是,头结点是不存数据的!
Node.h 文件的内容 上源码:
#ifndef NODE_H
#define NODE_H
#include
using namespace std;
template <class T>
class Node
{
public:
T data; //定义一个模板类的数据域;
Node *next; //定义一个指针域;
void printNode(); //打印节点数据
};
template <class T>
void Node::printNode()
{
cout<< data <#endif
用LinkList类将各节点串连起来,并实现对链表中节点的增、改、删、查的功能以及扩充的功能;
LinkList.h 文件的内容如下:
#ifndef LINKLIST_H
#define LINKLIST_H
#include "Node.h"
#include
#include
using namespace std;
//可参见顺序表的类声明
template <class T>
class LinkList
{
public:
LinkList(); //链表在创建时可以从堆中去申请到一块空间所以不用赋初值;
~LinkList();
void ClearList();
bool ListEmpty();
int ListLength();
int LocateElement( Node *pNode );
bool PriorElement( Node *pCurrentNode,Node *pPreNode );
bool NexitElement( Node *pCurrentNode,Node *pNextNode );
void ListTraverse();
bool ListInsertHead( Node *pNode );
bool ListInsertTail( Node *pNode );
bool ListInsert( int i,Node *pNode );
bool ListDelete( int i,Node *pNode );
bool GetElement( int i,Node *pNode );
private:
Node *p_List; //定义头结点;
int Length;
};
template <class T>
LinkList::LinkList()
{
p_List = new Node; //重堆中申请一个内存空间给头结点;
Length = 0;
//p_List->data = NULL;
p_List->next = NULL;
}
template <class T>
void LinkList::ClearList()
{
//思想是从头结点开始,从上一个节点的next指针去找到下一个节点的地址再删除,直到最后一个节点;
Node *currentNode = p_List->next;
while(currentNode != NULL) //用审犯人,交代下线的方式来理解就很容易;
{
Node *temp = currentNode->next;
delete currentNode;
currentNode = temp;
}
p_List->next = NULL; //将头节点的next置空;
Length = 0;
}
template <class T>
LinkList::~LinkList()
{
ClearList();
delete p_List;
p_List = NULL;
}
template <class T>
bool LinkList::ListEmpty()
{
if (Length == 0)
{
return true;
}
else
{
return false;
}
}
template <class T>
int LinkList::ListLength()
{
return Length;
}
template <class T>
//先定义插入操作后,后面方法定义更容易理解;
bool LinkList::ListInsertHead( Node *pNode )
{
Node *temp = p_List->next; //从头节点的下一个节点的地址 //首先保存头结点的指针域;
Node *newNode = new Node; //创建一个新节点;
if (newNode == NULL) //判断申请内存是否失败;
{
return false;
}
newNode->data = pNode->data; //将传入的节点数据传给新节点
newNode->next = temp; //把新节点作为下一个节点
p_List->next = newNode; //这两步为连接操作;
Length++;
return true;
}
template <class T>
bool LinkList::ListInsertTail( Node *pNode )
{
Node* currentNode = p_List; //从头节点开始访问;
while(currentNode->next != NULL)
{
currentNode = currentNode->next;
}
Node *newNode = new Node;
if (newNode == NULL)
{
return false;
}
newNode->data = pNode->data; //将传入的节点的数据保存到新节点
//currentNode->next = newNode;
newNode->next = NULL; //将新节点作为最后一个节点;
currentNode->next = newNode; //连接操作
Length++;
return true;
}
template <class T>
bool LinkList::ListInsert( int i,Node *pNode )
{
if (i<0 || i>Length)
{
return false;
}
else
{
Node *currentNode = p_List; //获取头结点,以便于遍历寻找下一个节点;
for (int k=0;knext;
}
Node *newNode = new Node;
if (newNode == NULL)
{
return false;
}
newNode->data = pNode->data;
newNode->next = currentNode->next;//使之前的下一个元素与新的元素相连;
currentNode->next = newNode;
Length++;
return true;
}
}
template <class T>
bool LinkList::ListDelete( int i,Node *pNode )
{
if(i<0 || i>=Length)
{
return false;
}
Node *currentNode = p_List;
Node *currentNodeBefore = NULL;
for (int k=0;k<=i;k++)
{
currentNodeBefore = currentNode; //将当前节点的首地址保存下来,;
currentNode = currentNode->next; //保持与之前元素的连接;
}
currentNodeBefore->next = currentNode->next; //删除i所示元素的下一个元素,即目标元素;
pNode->data = currentNode->data;
delete currentNode;
currentNode = NULL;
Length--;
return true;
}
template <class T>
bool LinkList::GetElement( int i,Node *pNode )
{
//获取指定位置的元素,将其传出来;
if (i<0 || i>Length)
{
return false;
}
Node * currentNode = p_List;
for (int k=0;k<=i;k++)
{
currentNode = currentNode->next;
}
pNode->data = currentNode->data;
return true;
}
template <class T>
int LinkList::LocateElement( Node *pNode )
{
Node *currentNode = p_List;
int count = 0; //头结点不存数据,应从头结点的next元素开始算;
while(currentNode != NULL)
{
currentNode = currentNode->next;
//改注释的代码块为下面的通讯录Damon的,执行通讯录时请将下面的if语句替换掉
//if (pNode->data == currentNode->data)
//{
// return count;
//}
if (pNode->data == currentNode->data)
{
return count;
}
else
{
return -1;
}
count++;
}
//cout<< "该链表中没有该元素!"<
//return -1;
}
template <class T>
bool LinkList::PriorElement( Node *pCurrentNode,Node *pPreNode )
{
Node *currentNode = p_List;
Node *tempNode = NULL;
while(currentNode =! NULL)
{
tempNode = currentNode;
currentNode = currentNode->next;
if (currentNode->data == pCurrentNode->data)
{
if (tempNode == p_List)
{
return false;
}
else
{
pPreNode->data = tempNode->data;
return true;
}
}
}
}
template <class T>
bool LinkList::NexitElement( Node *pCurrentNode,Node *pNextNode )
{
Node *currentNode = p_List;
while(currentNode =! NULL)
{
currentNode = currentNode->next;
if (currentNode.data == pCurrentNode->data)
{
if (currentNode->next == NULL)
{
return false;
}
else
{
pNextNode->data = currentNode->next->data; //头结点不算元素;
return true;
}
}
}
}
template <class T>
void LinkList::ListTraverse()
{
if ( Length == 0)
{
cout<<"error"<exit (1);
}
Node *currentNode = p_List;
while(currentNode->next != NULL)
{
currentNode = currentNode->next;
currentNode->printNode();
}
}
#endif
用刚才实现的链表打造一个 通讯录 ;
i. 写出people类,people.h 的源码如下:
#ifndef PERSON_H
#define PERSON_H
#include
#include
using namespace std;
class Person
{
friend ostream &operator<<(ostream &out,Person &person); //声明友元函数用于重载输出运算符;
public:
string name; //用于存联系人名字;
string phone; //用于存联系人电话;
//int age;
Person &operator=(Person &person); //重载比较运算符
bool operator==(Person &person); //重载==运算符
};
ostream &operator<<(ostream &out,Person &person)
{
out << person.name << "-------" << person.phone <return out;
}
Person & Person::operator=( Person &person )
{
this->name = person.name;
//this->age = person.age;
this->phone = person.phone;
return *this;
}
bool Person::operator==( Person &person )
{
if (this->name == person.name && this->phone == person.phone)
{
return true;
}
return false;
}
#endif
j. LinkList.cpp 文件的内容如下:
#include "LinkList.h"
#include "Person.h"
//#include "Node.h"
#include
using namespace std;
int menu()
{
cout<cout<<"---------功能表---------"<cout<<"------1.新建联系人------"<cout<<"------2.删除联系人------"<cout<<"------3.查询联系人------"<cout<<"------4.浏览联系人------"<cout<<"------5.退出通讯录------"<cout<int n = 0;
cout<< "请选择功能:" <cin>> n ;
if (n<0 || n>5)
{
cout<< "选择功能未开发......请重选!"<cout<< "请选择功能:" <return n;
}
void createPerson(LinkList *list)
{
Node node;
Person person;
cout<<"请输入新建联系人信息:"<cout<<"姓名: ";
cin>>person.name;
cout<<"电话: ";
cin>>person.phone;
node.data = person;
list->ListInsertTail(&node);
}
void deletePerson(LinkList *list)
{
Node node1,node2;
Person person;
cout<<"请输入需要删除的联系人信息:"<cout<<"姓名: ";
cin>>person.name;
node1.data = person;
int locate_i;
locate_i = list->LocateElement(&node1);
if (locate_i != -1)
{
list->ListDelete(locate_i,&node2);
cout<<"删除成功!"<return ;
}
cout<<"您的通讯录里没有这个人,请重选功能!"<return ;
}
void selectPerson( LinkList* list )
{
Node node1,node2;
Person person;
cout<<"请输入所查联系人的信息:"<cout<<"姓名: ";
cin>>person.name;
node1.data = person;
int locate_i;
locate_i = list->LocateElement(&node1);
if (locate_i != -1)
{
list->GetElement(locate_i,&node2);
cout<<"查询结果如下:"<return ;
}
else
{
cout<< "您的通讯录里没有该联系人!"<cout<<"请重选功能"<return ;
}
}
void allPerson( LinkList* list )
{
list->ListTraverse();
}
int main()
{
LinkList list;
int select = 0;
while(select != 5)
{
select = menu();
switch (select)
{
case 1:
cout<< "用户指令--->>新建联系人:"<list);
cout<<"新建成功!"<break;
case 2:
cout<<"用户指令--->>删除联系人: "<list);
//cout<<"删除成功!"<
break;
case 3:
cout<<"用户指令--->>查询联系人: "<list );
//cout<<"查询成功!"<
break;
case 4:
cout<<"用户指令--->>浏览联系人: "<list );
//cout<<"浏览成功!"<
}
}
exit(1);
system("pause");
return 0;
}
一个通过链表来实现的简易通讯录就这样实现了,作为一名程
序猿来说,此时的心情是谈10个女朋友也无法比拟的。真是美
滋滋啊~~~~~
第二夜:数据结构之——队列篇;
队列:队列是一种特殊的线性表,特殊之处在于它只
允许在表的前端(front)进行删除操作,而在表的后端(
rear)进行插入操作,和栈一样,队列是一种操作受限制
的线性表。
简言之就是一种先进先出(FIFO)的数据模型;可以理解
为一队人在买票,先来者站前面,先买好就先走,后来的
站后面,从前往后买;排在最前面的称作队头,最后面的
称为队尾!
#ifndef CIRQUEUE_H
#define CIRQUEUE_H
#include
using namespace std;
//const int CirQueueSize = 4;
template <class Datatype> //定义类模板;
class CirQueue
{
public:
CirQueue(int CirQueueSize); //构造函数
virtual ~CirQueue();
void ClearQueue(); //清除队列;
void QueueTraverse(); //遍历队列;
bool QueueEmpty(); //判空;
bool QueueFull(); //判满;
int QueueLength();
bool EnQueue(Datatype x); //入队
bool DeQueue(Datatype &x);//出队;
Datatype GetQueue(); //获取队头不出队;
private:
int *p_Queue; //队列(数组)的指针
int QueueSize; //容器(数组)的大小;
int Length; //队列的长度;
int Head; //队头
int Teal; //队尾
};
//const int CirQueueSize = 4;
template <class Datatype>
CirQueue::CirQueue(int CirQueueSize)
{
QueueSize = CirQueueSize;
try
{
p_Queue = new Datatype[QueueSize]; //在堆区申请一个数组用作队列元素的容器;
}
catch(...)
{
delete []p_Queue;
p_Queue = NULL;
//cout<< "队列创建失败,请重试!" <
}
Head = Teal =QueueSize-1; //队列开始时队头与队尾指针都指在同一位置;
Length = 0;
}
template <class Datatype>
CirQueue::~CirQueue()
{
delete []p_Queue;
p_Queue = NULL;
}
template <class Datatype>
void CirQueue::ClearQueue()
{
Head = Teal =QueueSize-1; //使队头与队尾都指在同一位置
Length = 0;
}
template <class Datatype>
bool CirQueue::QueueEmpty()
{
if (Length == 0)
{
return true;
}
else
{
return false;
}
}
template <class Datatype>
bool CirQueue::QueueFull()
{
if (Length == QueueSize)
{
return true;
}
else
{
return false;
}
}
template <class Datatype>
int CirQueue::QueueLength()
{
return Length;
}
template <class Datatype>
bool CirQueue::EnQueue(Datatype x)
{
if (QueueFull()) //入队时首先要判满;
{
return false;
}
else
{
p_Queue[Teal] = x; //在队尾位置放元素;
Teal = (Teal+1)%QueueSize; //放完元素后队尾指针自动往后滑一个长度,
记得要与队列长度取余作为新队尾,防止上溢;
Length++;
//cout<< "EnQueue Ok" <
return true;
}
}
template <class Datatype>
bool CirQueue::DeQueue(Datatype &x)
{
//int temp = Head;
if (QueueEmpty())
{
//cout<< "下溢" <
return false;
}
else
{
x = p_Queue[Head]; //将队头出队;
Head = (Head+1)%QueueSize;
Length--;
//return p_Queue[temp];
return true;
}
}
template <class Datatype>
Datatype CirQueue::GetQueue()
{
return p_Queue[Head];
}
template <class Datatype>
void CirQueue::QueueTraverse()
{
if (QueueEmpty())
{
//throw "这是一个空队列";
cout<< "这是一个空队列!" <//return false;
}
else
{
//从队头开始往后访问遍历;
for (int i = Head;icout << p_Queue[i%QueueSize] <//cout<<"********************************"<
}
}
}
#endif
2.一下是CirQueue.cpp调试文件的源代码:
#include
//#include
#include "CirQueue.h"
#include "CirQueue.cpp"
using namespace std;
int main()
{
CirQueue<int> MyQueue(4);
bool EnQueue_1_bool;
EnQueue_1_bool = MyQueue.EnQueue(1);
cout<<"EnQueue_1_bool: "<< EnQueue_1_bool <bool EnQueue_2_bool;
EnQueue_2_bool = MyQueue.EnQueue(2);
cout<<"EnQueue_2_bool: "<< EnQueue_2_bool <int MyQueue_head;
MyQueue_head = MyQueue.GetQueue();
cout<<"MyQueue_head: "<int MyQueue_length;
MyQueue_length = MyQueue.QueueLength();
cout<<"MyQueue_length: "<< MyQueue_length <int x;
MyQueue.DeQueue(x);
cout<<"DeQueue: x = "<< x <int y;
MyQueue.DeQueue(y);
cout<<"DeQueue: y = "<< y <bool Empty_bool;
Empty_bool = MyQueue.QueueEmpty();
cout<<"Empty_bool: "<< Empty_bool <10);
MyQueue.EnQueue(20);
MyQueue.EnQueue(30);
MyQueue.EnQueue(40);
MyQueue.QueueTraverse();
system("pause");
return 0;
}
第三夜:数据结构之——栈篇
栈:栈是限定仅在表头(栈顶)进行插入和删除操作的线性表”栈“者,存储货物或供旅客住宿的地方,可引申为仓库、中转站,所以引入到计算机领域里,就是指数据暂时存储的地方,所以才有进栈、出栈的说法。
栈的特点:后进先出(LIFO)这个很容易理解的~~先进电梯的人后出来,后进电梯的人先出来。
栈其实很简单,只要掌握好top指针的技巧就很容易理解了,top指针就相当于桶里的一只小船,水涨船高,水降船降;
#ifndef MYSTACK_H
#define MYSTACK_H
template <class Datatype>
class Mystack
{
public:
Mystack(Datatype size);
~Mystack();
bool stackEmpty();
bool stackFull();
bool push(Datatype x); //入栈;
bool pop(Datatype &x); //出栈;
void clearStack();
int stackLength();
void stackTraverse(bool isFromeButtom);
private:
Datatype *p_Buffer;//栈的入口
int Size; //栈的大小
int Top; //栈顶,在栈中极为重要;
};
template <class Datatype>
Mystack::Mystack( Datatype size )
{
Size = size;
Top = 0;
p_Buffer = new Datatype[Size];//在堆区申请一个数组来实现栈
}
template <class Datatype>
Mystack::~Mystack()
{
delete []p_Buffer;
p_Buffer = NULL;
}
template <class Datatype>
bool Mystack::stackEmpty()
{
if (Top == 0) //栈顶可以实时的反应出栈的情况
{
return true;
}
else
{
return false;
}
}
template <class Datatype>
bool Mystack::stackFull()
{
if (Top == Size)
{
return true;
}
else
{
return false;
}
}
template <class Datatype>
bool Mystack::push( Datatype x )
{
if (!stackFull())
{
p_Buffer[Top] = x; //每入栈一个元素后top自动向上移动一个位置,以作为下一次入栈的栈顶指针;
Top++;
return true;
}
else
{
return false;
}
}
template <class Datatype>
bool Mystack::pop(Datatype &x)
{
if (stackEmpty())
{
return false;
}
else
{
Top--;
x = p_Buffer[Top];//将栈顶指针下滑一位,指向出栈元素
}
}
template <class Datatype>
void Mystack::clearStack()
{
Top = 0;
}
template <class Datatype>
int Mystack::stackLength()
{
return Top;
}
template <class Datatype>
void Mystack::stackTraverse(bool isFromeButtom)
{
if (isFromeButtom)
{
for (int i=0;icout<< p_Buffer[i] ;//<< ',';
}
}
else
{
for (int i=Top-1;i>=0;i--)
{
cout<< p_Buffer[i] ;//<< ',';
}
}
}
#endif
#include
#include "Mystack.h"
using namespace std;
int main()
{
Mystack<char> mystack(5);
mystack.push('H');
mystack.push('e');
mystack.push('l');
mystack.push('l');
mystack.push('o');
mystack.stackTraverse(true);
cout<< endl;
cout<< mystack.stackLength() <char x = 'x';
cout<< mystack.pop(x) <cout<< mystack.stackLength() <cout<< mystack.stackEmpty() <cout<< mystack.stackEmpty() <char y = 't';
cout<< mystack.pop(x) <"pause");
return 0;
}
#include
#include "Mystack.h"
using namespace std;
#define BINARY 2
#define OCTONARY 8
#define HEXADECIMAL 16
//用辗转相初法求模;
//template
void to_binary(int n,int mod,Mystack<int> *p_b_stack)
{
while (n != 0)
{
mod = n % BINARY;
p_b_stack->push(mod);
n = n / BINARY;
}
p_b_stack->stackTraverse(false);
cout<void to_octonary(int n,int mod,Mystack<int> *p_o_stack)
{
while (n != 0)
{
mod = n % OCTONARY;
p_o_stack->push(mod);
n = n / OCTONARY;
}
p_o_stack->stackTraverse(false);
cout<void to_hexadecimal(int n,int mod,Mystack<int> *p_h_stack)
{
while (n != 0)
{
mod = n % HEXADECIMAL;
p_h_stack->push(mod);
n = n / HEXADECIMAL;
}
char str[] = "0123456789ABCDEF";
int i=0;
while(!p_h_stack->stackEmpty())
{
p_h_stack->pop(i);
cout<< str[i] ;
}
//p_e_stack->stackTraverse(false);
cout<int main()
{
Mystack<int> *p_b_stack = new Mystack<int>(20);
Mystack<int> *p_o_stack = new Mystack<int>(20);
Mystack<int> *p_h_stack = new Mystack<int>(20);
int n = 1;
cin>>n;
int mod = 0;
to_binary(n,mod,p_b_stack);
to_octonary(n,mod,p_o_stack);
to_hexadecimal(n,mod,p_h_stack);
delete p_b_stack;
delete p_o_stack;
delete p_h_stack;
p_b_stack = NULL;
p_o_stack = NULL;
p_h_stack = NULL;
system("pause");
return 0;
}
第四夜:数据结构之——树篇
树:树是由根结点和若干颗子树构成的。树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构。在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点,或称为树根。
树的种类:
无序树:树中任意节点的子结点之间没有顺序关系,这种树称为无序树,也称为自由树;
有序树:树中任意节点的子结点之间有顺序关系,这种树
称为有序树;
二叉树:每个节点最多含有两个子树的树称为二叉树;
完全二叉树
满二叉树
霍夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二
叉树;
下面我重点来看二叉树:
二叉树:二叉树在图论中是这样定义的:二叉树是一个连
通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满
足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一
的父结点,和最多2个子结点。
我主要总结了二叉树的数组实现和链表实现两种方式;
**注意**:学习二叉树我们的脑海中需要有父节点与左右孩子节点的思想
//以数组元素为节点;
#ifndef TREE_H
#define TREE_H
#include
using namespace std;
template <class T>
class Tree
{
public:
Tree(int size,T *pRoot); //给定数的大小,以及根
~Tree();
T *SerchNode(int nodeIndex); //搜寻节点
bool AddNode(int nodeIndex,int derection,T *node);//添加节点;
bool DeleteNode(int nodexIndex,T *node); //删除节点
void TreeTreaverse();
private:
T *p_Tree;
int Size;
};
template <class T>
Tree::Tree( int size,T *pRoot )
{
Size = size;
p_Tree = new T[Size]; //在堆区申请一个数组来实现二叉树
for (int i=0;i//对每个节点初始化
{
p_Tree[i] = NULL;
}
p_Tree[0] = *pRoot; //定义根节点
}
template <class T>
Tree::~Tree()
{
delete []p_Tree;
p_Tree = NULL;
}
template <class T>
T * Tree::SerchNode( int nodeIndex )
{
if (nodeIndex<0 || nodeIndex>Size)
{
return NULL;
}
if (NULL == p_Tree[nodeIndex])
{
return NULL;
}
return &p_Tree[nodeIndex];
}
//添加为左节点index = nodeIndex*2 + 1;右节点: index = nodeIdex*2 + 2;
template <class T>
bool Tree::AddNode( int nodeIndex,int derection,T *node )
{
if (nodeIndex<0 || nodeIndex>Size)
{
return false;
}
if (NULL == p_Tree[nodeIndex])
{
return false;
}
if (derection == 0)
{
if (nodeIndex*2 + 1>Size)
{
return false;
}
if (NULL != p_Tree[nodeIndex*2 + 1])
{
return false;
}
if ( nodeIndex*2 + 1 <= Size )
{
p_Tree[nodeIndex*2 + 1] = *node;
}
}
if (derection == 1)
{
if (nodeIndex*2 + 2>Size)
{
return false;
}
if (NULL != p_Tree[nodeIndex*2 + 2])
{
return false;
}
if ( nodeIndex*2 + 2 <= Size )
{
p_Tree[nodeIndex*2 + 2] = *node;
}
}
return true;
}
template <class T>
bool Tree::DeleteNode( int nodeIndex,T *node )
{
if (nodeIndex<0 || nodeIndex>Size)
{
return false;
}
if (NULL == p_Tree[nodeIndex])
{
return false;
}
*node = p_Tree[nodeIndex];
p_Tree[nodeIndex] = NULL;
return true;
}
template <class T>
void Tree::TreeTreaverse()
{
for (int k=0;kcout<< p_Tree[k] << " " <#endif
下面是二叉树的链表实现:
#ifndef NODE_H
#define NODE_H
#include <iostream>
using namespace std;
template <class T>
class Node
{
public:
Node();
T data; //数据域;
int index;
Node<T> *pLchild; //左孩子节点指针;
Node<T> *pRchild; //右孩子节点指针;
Node<T> *pParent; //父节点;
void DeleteNode();
void PreorderTraversal(); //前序遍历
void InorderTraversal(); //中序遍历
void PostorderTraversal(); //后序遍历
Node<T> *SearchNode(int nodeIndex); //寻找节点
};
template <class T>
Node<T>::Node()
{
index = 0;
data = NULL;
pLchild = NULL;
pRchild = NULL;
pParent = NULL;
}
template <class T>
Node<T> * Node<T>::SearchNode( int nodeIndex )
{
Node<T> *temp;
if ( this->index == nodeIndex ) //如果所给节点为当前节点,便返回当前节点;
{
return this;
}
if (this->pLchild != NULL) //首先判断左孩子存不存在
{
//看看左孩子是否为所找;
if (this->pLchild->index == nodeIndex)
{
return this->pLchild;
}
else //否则就调用递归查找
{
temp = this->pLchild->SearchNode(nodeIndex);
if (temp != 0)
{
return temp;
}
}
}
//再判断右孩子存不存在
if (this->pRchild != NULL)
{
//判断右孩子是否为所找
if (this->pRchild->index == nodeIndex)
{
return this->pRchild;
}
//否则在调用递归查询
else
{
temp = this->pRchild->SearchNode(nodeIndex);
if (temp != 0)
{
return temp;
}
}
}
return NULL;
}
template <class T>
void Node<T>::DeleteNode()
{
//判断左孩子是否为空,若不为空就调用递归删除
if (this->pLchild != NULL)
{
this->pLchild->DeleteNode();
}
//判断左孩子是否为空,若不为空就调用递归删除
if (this->pRchild != NULL)
{
this->pRchild->DeleteNode();
}
//判断该节点的父节点是否为空,因为空节点没有左右孩子
if (this->pParent != NULL)
{
//判断该节点是否为父节点的左孩子,并删除
if (this->pParent->pLchild == this)
{
this->pParent->pLchild = NULL;
}
//判断该节点是否为父节点的右孩子,并删除
if (this->pParent->pRchild == this)
{
this->pParent->pRchild = NULL;
}
}
delete this; //所有的子节点都删除完了,再删除自己
}
template <class T>
void Node<T>::PreorderTraversal()
{
//前序遍历,根-左-右
cout<< this->index << "--->" << this->data <<endl;
//若左孩子存在,调用递归进行前序遍历
if (this->pLchild != NULL)
{
this->pLchild->PreorderTraversal();
}
//若右孩子存在,调用递归进行前序遍历
if (this->pRchild != NULL)
{
this->pRchild->PreorderTraversal();
}
}
template <class T>
void Node<T>::InorderTraversal()
{
//中序遍历,左-根-右
if (this->pLchild != NULL)
{
this->pLchild->InorderTraversal();
}
cout<< this->index << "--->" << this->data <<endl;
if (this->pRchild != NULL)
{
this->pRchild->InorderTraversal();
}
}
template <class T>
void Node<T>::PostorderTraversal()
{
//后序遍历,左-右-根
if (this->pLchild != NULL)
{
this->pLchild->PostorderTraversal();
}
if (this->pRchild != NULL)
{
this->pRchild->PostorderTraversal();
}
cout<< this->index << "--->" << this->data <<endl;
}
#endif
再将每个节点按照二叉树的关系联合起来,所以用一个LinkListTree类进行整合;LinkListTree.h 的源码如下:
#ifndef TREE_H
#define TREE_H
#include "Node.h"
#include
using namespace std;
template <class T>
class LinkListTree
{
public:
LinkListTree();
~LinkListTree();
Node *SearchNode(int nodeIndex);
bool AddNode(int nodeIndex,int derection,Node *pNode);
bool DeleteNode(int nodexIndex,Node *pNode);
void PreorderTraversal();
void InorderTraversal();
void PostorderTraversal();
private:
Node *pRoot;
};
template <class T>
LinkListTree::LinkListTree()
{
pRoot = new Node;
}
template <class T>
LinkListTree::~LinkListTree()
{
pRoot->DeleteNode();
//delete pRoot;
//pRoot = NULL;
}
template <class T>
Node * LinkListTree::SearchNode( int nodeIndex )
{
return pRoot->SearchNode(nodeIndex);
}
template <class T>
bool LinkListTree::AddNode( int nodeIndex,int derection,Node *pNode )
{
Node *temp = SearchNode(nodeIndex);
if (temp == NULL)
{
return false;
}
Node *newNode = new Node;
if (newNode == NULL)
{
return false;
}
newNode->index = pNode->index;
newNode->data = pNode->data;
newNode->pParent = temp;
if (derection == 0)
{
temp->pLchild = newNode;
}
if (derection == 1)
{
temp->pRchild = newNode;
}
return true;
}
template <class T>
bool LinkListTree::DeleteNode( int nodeIndex,Node *pNode )
{
Node *temp = SearchNode(nodeIndex);
if (temp == NULL)
{
return false;
}
if (pNode != NULL)
{
pNode->data = temp->data;
}
temp->DeleteNode();
return true;
}
template <class T>
void LinkListTree::PreorderTraversal()
{
pRoot->PreorderTraversal();
}
template <class T>
void LinkListTree::InorderTraversal()
{
pRoot->InorderTraversal();
}
template <class T>
void LinkListTree::PostorderTraversal()
{
pRoot->PostorderTraversal();
}
#endif
第五夜:数据结构之——图篇
图:数据元素间的关系是任意的。其他数据结构(如树、线性表等)都有明确的条件限制,而图形结构中任意两个数据元素间均可相关联。常用来研究生产流程、施工计划、各种网络建设等问题。
1 理解图的两大存储结构
1-1 邻接矩阵
1-2 邻接表
注意:邻接表中,指针数组里的每一个指针都是一个单链表的头指针
注意:单链表里每个节点里存储的是图中每条边的信息。
2 理解图的遍历算法
2-1 深度优先遍历
2-2 宽度优先遍历
注意:又叫广度优先算法,需要一个队列
3 图的最小代价生成树算法
3-1 普里姆算法 。
3-2 克鲁斯卡尔算法
注意:克鲁斯卡尔的时间复杂度
下面我将总结由顶点数组(存节点)与邻接矩阵(存弧或边的相关
信息)构成的图;
#ifndef NODE_H
#define NODE_H
template<class T>
class Node
{
public:
Node(T data = 0);
T cData; //存数据
bool bIsVisited; //节点的访问属性;
};
template<class T>
Node::Node( T data )
{
cData = data;
bIsVisited = false;
}
#endif
#ifndef EDGE_H
#define EDGE_H
class Edge
{
public:
Edge(int nodeIndexA = 0,int nodeIndexB = 0,int weightValue = 0);
int NodeIndexA; //节点A
int NodeIndexB; //节点B
int Weightvalue; //边的权值
bool bSelected; //边的访问属性
};
Edge::Edge( int nodeIndexA /*= 0*/,int nodeIndexB /*= 0*/,int weightValue /*= 0*/ )
{
NodeIndexA = nodeIndexA;
NodeIndexB = nodeIndexB;
Weightvalue = weightValue;
bSelected = false;
}
#endif
#ifndef CMAP_H
#define CMAP_H
/*
Map 由顶点数组(存节点) 与 邻接矩阵(存弧或边的相关信息)构成;
*/
#include "Node.h"
#include "Edge.h"
#include
#include
using namespace std;
template <class T>
class CMap
{
public:
CMap(int capacity);
~CMap();
bool addNode(Node *pNode);
void resetNode();
bool setValueToMatrixForDirectedGraph(int row,int col,int val = 1);
bool setValueToMatrixForUndirectedGraph(int row,int col,int val = 1);
void printMatrix();
void depthFirstTraverse(int nodeIndex); //深度优先遍历;
void breadthFirstTraverse(int nodeIndex); //广度优先遍历;
void primTree(int nodeIndex); //普利姆生成树算法;
private:
bool getValueFromeMatrix(int row,int col,int &val); //从函数中获取权值;
void breadthFirstTraverseImpl(vector<int> preVec); //广度优先遍历实现函数;
int getMinEdge(vector edgeVec);
private:
int Capacity;
int NodeCount;
Node *NodeArray; //存放节点(顶点)的数组;
int *pMatrix; //存放弧关系信息的邻接矩阵;
Edge *pEdge;
};
template <class T>
CMap::CMap( int capacity )
{
Capacity = capacity;
NodeCount = 0;
NodeArray = new Node[Capacity];
pMatrix = new int[Capacity*Capacity];
for (int i=0;inew Edge[Capacity-1]; //用于存放最小生成树中的所有边;
}
template <class T>
CMap::~CMap()
{
delete []NodeArray;
NodeArray = NULL;
delete []pMatrix;
pMatrix = NULL;
delete []Edge;
pEdge = NULL;
}
template <class T>
bool CMap::addNode( Node *pNode )
{
if (pNode == NULL)
{
return false;
}
NodeArray[NodeCount].cData = pNode->cData;
NodeCount++;
return true;
}
template <class T>
void CMap::resetNode()
{
for (int i=0;ifalse;
}
}
template <class T>
bool CMap::setValueToMatrixForDirectedGraph( int row,int col,int val /*= 1*/ )
{
if (row>=0 && row=0 && colreturn true;
}
return false;
}
template <class T>
bool CMap::setValueToMatrixForUndirectedGraph( int row,int col,int val /*= 1*/ )
{
if (row>=0 && row=0 && colreturn true;
}
return false;
}
template <class T>
bool CMap::getValueFromeMatrix( int row,int col,int &val )
{
if (row>=0 && row=0 && colreturn true;
}
return false;
}
template <class T>
void CMap::printMatrix()
{
for (int i=0;ifor (int j=0;jcout<< pMatrix[i*Capacity + j] <<' ';
}
cout<template <class T>
void CMap::depthFirstTraverse( int nodeIndex )
{
int val = 0;
cout<< NodeArray[nodeIndex].cData <<' ';
NodeArray[nodeIndex].bIsVisited = true;
for (int i=0;iif (val == 1)
{
if (NodeArray[i].bIsVisited)
{
continue;
}
else
{
depthFirstTraverse(i);
}
}
else
{
continue;
}
}
}
template <class T>
void CMap::breadthFirstTraverse( int nodeIndex )
{
cout<< NodeArray[nodeIndex].cData <<' ';
NodeArray[nodeIndex].bIsVisited = true;
vector<int> curVec;
curVec.push_back(nodeIndex);
breadthFirstTraverseImpl( curVec );
}
template <class T>
void CMap::breadthFirstTraverseImpl( vector<int> preVec )
{
int value = 0;
vector<int> curVec;
for (int j=0;j<(int)preVec.size();j++)
{
for (int i=0;iif (value != 0)
{
if (NodeArray[i].bIsVisited)
{
continue;
}
else
{
cout<' ';
NodeArray[i].bIsVisited = true;
curVec.push_back(i);
}
}
}
}
if (curVec.size() != 0)
{
breadthFirstTraverseImpl(curVec);
}
else
{
return;
}
}
template <class T>
int CMap::getMinEdge( vector edgeVec )
{
int minWeight = 0;
int edgeIndex = 0;
int i = 0;
for (;i<(int)edgeVec.size();i++)
{
if (!edgeVec[i].bSelected)
{
minWeight = edgeVec[i].Weightvalue;
edgeIndex = i;
//cout<<"aaaa"<
break;
}
}
if (edgeIndex = 0)
{
return -1;
}
for (;i<(int)edgeVec.size();i++)
{
if (!edgeVec[i].bSelected)
{
if (minWeight>edgeVec[i].Weightvalue)
{
minWeight = edgeVec[i].Weightvalue;
edgeIndex = i;
}
}
}
return edgeIndex;
}
//普利姆生成树算法;
template <class T>
void CMap::primTree( int nodeIndex )
{
int value = 0; //存放边的权值;
int edgeCount = 0;
vector<int> nodeVec; //存放点的(索引)集合;
vector edgeVec; //存放边的集合;
nodeVec.push_back(nodeIndex);
cout<< NodeArray[nodeIndex].cData <while (edgeCount < Capacity-1)
{
int temp = nodeVec.back();
for (int i=0;i//cout<<"value:"<
if (value != 0)
{
//cout<<"test"<
if (NodeArray[i].bIsVisited)
{
continue;
}
else
{
Edge edge(temp,i,value);//生成一个边对象;
edgeVec.push_back(edge);
}
}
}
//上一个for循环后获得了所有顶点的有关边,存到了edge数组里;
//接下来从可选边集合中找出权值最小的边,添加到最小边数组中;
int edgeIndex = getMinEdge(edgeVec);
//cout<<"edgeIndex:"<
edgeVec[edgeIndex].bSelected = true;
pEdge[edgeCount] = edgeVec[edgeIndex];
cout<"---" <"---" ;
cout<int nextNodeIndex = edgeVec[edgeIndex].NodeIndexB;//获得与该边相连接的下一个节点索引;
nodeVec.push_back(nextNodeIndex);//将该索引添加到存放生成树的节点的数组中,以便下次遍历;
NodeArray[nextNodeIndex].bIsVisited = true;
cout<#endif
以上便是一个小白十天学习数据结构的基本所获,在这十天中
有曾撞了无数次墙,debug了无数次,有时调Bug调到绝望,也
曾想过放弃;当然整个过程也充满了惊喜,一边是对知识和技
巧的惊,一边是对获得之后的喜,可谓是收获了满满的成就感
,最终还是坚持到了图篇!
另外,特别值得一提的是,我第一次写了一篇真正属于自己的
学习总结,还一不小心就写了这么长!在这里小小的鼓励一下
自己。。。。。。当然我更希望各位朋友多多指教,有不对的
地方还望多多包容与纠正~~感激不尽!
最后,我还想说的是,感谢慕课网以及里面授教的james_yuan老师,james老师教的的真的是一级棒,不经思路清晰,更重要的是细微入至,可谓是手把手教学啊!文中的绝大部分代码都是我跟着james老师学习之后的成果;再次给大家推荐james老师在慕课网的数据结构课程,真乃良心课程~~~
希望本文章能给和我一样不甘平凡的人带来一丝丝的帮助,那将是我的万分荣幸!
附:慕课网James老师课程:[1]https://www.imooc.com/course/list?c=cplusplus