数据结构学习


一、线性表

1.顺序储存结构的插入与删除

(1)获取元素

#include<iostream>

#include<string>

using namespace std;

class list{
public:
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    int length;
    list(){length=sizeof(a)/4;}
};
//获取元素操作
int GetNum(list t, int length, int *p){
    //判断线表是否长度为0
    if(length==0){
        return 0;
    }
    int m;
    cout << "输入获取哪个元素" << endl;
    cin>>m;
    //确保正确输入获取的元素下标
    while(m>length || m<0){
        cout << "重新输入 " <<endl;
        cin>>m;
    }
    length=m;
    *p=t.a[length-1];
    return *p;
}
int main(){
    list t;
    int q;
    q=GetNum(t,t.length,&q);
    cout << "这个数是" <<q<<endl;
}

(2)插入

#include<iostream>

using namespace std;

#define MAXSIZE 100
class list{
private:
    int data[MAXSIZE];
    int length=0;
public:
    void inlist(int len);
    void putlist(int len);
    void onlist(int len);
};
int main()
{
    list T;
    int len;
    cout <<"请输入线性表的长度"<<endl;
    cin>>len;
    T.inlist(len);
    T.putlist(len);
    T.onlist(len);
}
inline void list::inlist(int len){
    if(len<1||len>MAXSIZE){
        cout <<"输入不规范"<<endl;
    }else
    {
        cout <<"输入元素"<< endl;
        for(int i=0;i<len;i++){
            cin>>data[i];
        }
        length=len;
    }
}
inline void list::putlist(int len){
    cout << "输入的元素为" <<  endl;
    for(int i=0;i<len;i++){
        cout <<data[i]<<endl;
    }
}
inline void list::onlist(int len){
    int n,point;
    cout <<"插入的个数"<<endl;
    cin >> n;
    if(len+n>=MAXSIZE && n>len+1){
        cout <<"插入失败"<< endl;
    }else
    {
        cout <<"插入成功"<<endl;
        for(int i=0;i<n;i++){
            cout <<"插入的第"<<i+1<<"个数的位置"<<endl;
            cin>>point;
            cout <<"插入的数字是"<<endl;
            for(int j=length-1;j>=point-1;j--){
                data[j+1]=data[j];
            }    
            cin>>data[point-1];
            length++;
            list::putlist(length);
        }
    }
}

(3)删除

#include<iostream>

using namespace std;

#define MAXSIZE 100
class list{
private:
    int data[MAXSIZE];
    int length=0;
public:
    void inlist(int len);
    void putlist(int len);
    void onlist(int len);
};
int main()
{
    list T;
    int len;
    cout <<"请输入线性表的长度"<<endl;
    cin>>len;
    T.inlist(len);
    T.putlist(len);
    T.onlist(len);
}
inline void list::inlist(int len){
    if(len<1||len>MAXSIZE){
        cout <<"输入不规范"<<endl;
    }else
    {
        cout <<"输入元素"<< endl;
        for(int i=0;i<len;i++){
            cin>>data[i];
        }
        length=len;
    }
}
inline void list::putlist(int len){
    cout << "输入的元素为" <<  endl;
    for(int i=0;i<len;i++){
        cout <<data[i]<<endl;
    }
}
inline void list::onlist(int len){
    int n,point;
    cout <<"删除的个数"<<endl;
    cin >> n;
    if(n>=MAXSIZE || n<0 ){
        cout <<"删除失败"<< endl;
    }else
    {
        cout <<"删除成功"<<endl;
        for(int i=0;i<n;i++){
            cout <<"删除的第"<<i+1<<"个数的位置"<<endl;
            cin>>point;
            for(int j=point;j<=length-1;j++){
                data[j-1]=data[j];
            }    
            length--;
            list::putlist(length);
        }
    }
}

优缺点

2.链式储存结构

头指针与头节点

单链表

(1)初始化赋值

头插法:

#include<iostream>
using namespace std;
typedef struct Node{
    int date;
    struct Node* next;
}Node,*PNode;//Node 相当于struct Node , *PNode 相当于struct Node*
//用PNode主要为了强调这是一个单链表,用Node*主要强调这是一个节点

PNode ListNodeInit(){
    Node* L;
    L=(Node* )malloc(sizeof(Node));//申请头节点空间
    L->next=NULL;
    int a;
    cout<<"请输入节点数字"<<endl;
    cin>>a;
    for(int i=0;i<a;i++){
        int t;
        cin>>t;
        Node* p;//创建节点
        p=(Node* )malloc(sizeof(Node));
        p->date=t;
        p->next=L->next;
        L->next=p;
        int m;
    }
    return L;
}
void ListPrint(){
    Node* O;
    Node* Y=ListNodeInit();
    O=Y->next;
    while(O){
        cout<<O->date<<" ";
        O=O->next;
    }
}

int main(){
    ListPrint();
    return 0;
}

用头插法的遍历输出是逆序输出的,头插法是把每一个节点放在第一位,所以后面放在第一位的就会把之前的第一位挤到后面去

通过调试数据可以看出链表结构如下

L链表

输出结果如下

尾插法:

PNode ListNodeInit(){
    Node* L;
    Node* r;//定义尾指针,每次都指向最后一个节点
    L=(Node* )malloc(sizeof(Node));//申请头节点空间
    L->next=NULL;
    r=L;//尾指针指向头节点
    int a;
    cout<<"请输入节点数"<<endl;
    cin>>a;
    for(int i=0;i<a;i++){
        int t;
        cin>>t;
        Node* p;//创建节点
        p=(Node* )malloc(sizeof(Node));
        p->date=t;
        r->next=p;//把新节点插入到r节点之后
        r=p;//尾指针指向下一个节点
    }
    r->next=NULL;
    return L;
}

图解

尾插的聊表结构如下:

输出结果

(2)查找

按位查找,返回第i个元素

// 按位查找,返回第i个数据
Node *GetData_1(PNode L, int i)
{
    if (i < 0)
    {
        return NULL;
    }
    Node *p;   // p指向当前指向的节点
    int j = 0; // 计数,记录当前p指向的是第几个节点
    p = L;     // p开始指向头节点
    while (p != NULL && j < i)
    {
        p = p->next;
        j++;
    }
    return p;
}

按值查找,返回第i个数据

Node *GetData_2(PNode L, int e)
{
    Node *p;   // p指向当前指向的节点
    p = L->next;     // 从第一个数据开始查找,头节点是没有数据域的
    while(p!=NULL && p->date != e){//这里两个条件顺序不能变,不然会报错
        p = p->next;
    }
    return p;
}

查表长

int GetListLen(PNode L){
    Node* s = L;
    int num=0;
    while(s->next!=NULL){
        s = s->next;
        num++;
    }
    return num;
}
(3)插入

指定节点的后插操作

bool Set_Behind(Node* p,int i){
    if(p==NULL){//这里是防止在按位插入数据操作前的GetData()传入表尾的数据的NULL
        return false;
    }
    Node* s = (Node*)malloc(sizeof(PNode));//开辟一块空间
    s->date = i;
    s->next = p->next;
    p->next =s;
    return true;
}

按位插入数据

bool Set_Wei(PNode L,int i){
    Node *p = GetData_1(L, i-1);//这里要传入i-1,因为插入数据的时候是前一个数据的next指向的
    int t;
    cout <<"插入的数据是:";
    cin>>t; 
    if(Set_Behind(p,t)){
        return true;//如果插入成功就返回真
    }else{
        return false;
    }
}
(4)删除

指定节点p的删除

这个操作的原理就是实际上就是把p后一个节点的数据搬到p节点,实现将p节点删除

bool ListDel_1(Node* p){
    if(p==NULL){
        return false;
    }
    Node* s = p->next;//让节点s指向p的下一个节点
    p->date = p->next->date;//让p下一个节点的数据将p覆盖掉
    p->next = s->next;//把p的指针域换成下一个节点的指针域
    free(s);//释放s节点
    return true;
}

该代码有点bug,p->date = p->next->date,如果是删除最后一个元素,那么最后指向的NULL是没有数据域的。

按位删除节点

bool ListDel_2(PNode L,int i){
    Node *p = GetData_1(L, i);//这里要传入i
    if(ListDel_1(p)){
        return true;//如果插入成功就返回真
    }else{
        return false;
    }
}

完整代码

#include <iostream>
using namespace std;
typedef struct Node
{
    int date;
    struct Node *next;
} Node, *PNode; // Node 相当于struct Node , *PNode 相当于struct Node*
// 用PNode主要为了强调这是一个单链表,用Node*主要强调这是一个节点

// 尾插法创建链表
PNode ListNodeInit()
{
    Node *L;
    Node *r;                          // 定义尾指针,每次都指向最后一个节点
    L = (Node *)malloc(sizeof(Node)); // 申请头节点空间
    L->next = NULL;
    r = L; // 尾指针指向头节点
    int a;
    cout << "请输入节点数" << endl;
    cin >> a;
    for (int i = 0; i < a; i++)
    {
        int t;
        cin >> t;
        Node *p; // 创建节点
        p = (Node *)malloc(sizeof(Node));
        p->date = t;
        r->next = p; // 把新节点插入到r节点之后
        r = p;       // 尾指针指向下一个节点
    }
    r->next = NULL;
    return L;
}

// 按位查找,返回第i个数据
Node *GetData_1(PNode L, int i)
{
    if (i < 0)
    {
        return NULL;
    }
    Node *p;   // p指向当前指向的节点
    int j = 0; // 计数,记录当前p指向的是第几个节点
    p = L;     // p开始指向头节点
    while (p != NULL && j < i)
    {
        p = p->next;
        j++;
    }
    return p;
}
//按值查找,返回第i个数据
Node *GetData_2(PNode L, int e)
{
    Node *p;   // p指向当前指向的节点
    p = L->next;     // 从第一个数据开始查找,头节点是没有数据域的
    while(p != NULL && p->date != e){
        p = p->next;
    }
    return p;
}
//返回表长
int GetListLen(PNode L){
    Node* s = L;
    int num=0;
    while(s->next!=NULL){
        s = s->next;
        num++;
    }
    return num;
}
//指定节点的后插操作
bool Set_Behind(Node* p,int t){
    if(p==NULL){
        return false;
    }
    Node* s = (Node*)malloc(sizeof(PNode));//开辟一块空间
    s->date = t;
    s->next = p->next;
    p->next =s;
    return true;
}
//按位插入数据
bool Set_Wei(PNode L,int i){
    Node *p = GetData_1(L, i-1);//这里要传入i-1,因为插入数据的时候是前一个数据的next指向的
    int t;
    cout <<"插入的数据是:";
    cin>>t; 
    if(Set_Behind(p,t)){
        return true;//如果插入成功就返回真
    }else{
        return false;
    }
}
//指定节点的删除
bool ListDel_1(Node* p){
    if(p==NULL){
        return false;
    }
    Node* s = p->next;//让节点s指向p的下一个节点
    p->date = p->next->date;//让p下一个节点的数据将p覆盖掉
    p->next = s->next;//把p的指针域换成下一个节点的指针域
    free(s);//释放s节点
    return true;
}
//按位删除数据
bool ListDel_2(PNode L,int i){
    Node *p = GetData_1(L, i);//这里要传入i
    if(ListDel_1(p)){
        return true;//如果插入成功就返回真
    }else{
        return false;
    }
}

// 遍历链表
void ListPrint(PNode L)
{
    Node *s;
    s = L->next; // 让s指向第一个元素
    while (s != NULL)
    {
        cout << s->date << " ";
        s = s->next;
    }
}

int main()
{
    PNode L = ListNodeInit();
    cout << "------------------------------" << endl;
    cout<<"遍历这个表"<<endl;
    ListPrint(L);
    cout << endl;
    cout << "------------------------------" << endl;
    int len = GetListLen(L);
    cout <<"这个表长为:"<<len<<endl;
    cout << "------------------------------" << endl;
    int i;
    cout << "请输入要查找的位数:";
    cin >> i;
    Node *t1 = GetData_1(L, i);
    cout << "第" << i << "个节点的数据是" << t1->date << endl;
    cout << "------------------------------" << endl;
    int e;
    cout << "请输入要查找的数:";
    cin >> e;
    Node *t2 = GetData_2(L, e);
    if(t2!=NULL){
        cout <<"找到了数据为"<<e<<"的元素"<<endl;
    }else{
        cout<<"没有找到值为"<<e<<"的元素"<<endl;
    }
    cout << "------------------------------" << endl;
    cout << "请输入要插入的位数:";
    cin >> i;
    bool ret = Set_Wei(L, i);
    if(ret){
        cout << "插入成功"<< endl;
    }else{
        cout << "插入失败"<< endl;
    }
    cout<<"插入后遍历这个表"<<endl;
    ListPrint(L);
    cout << "------------------------------" << endl;
    cout << "请输入要删除的位数:";
    cin >> i;
    bool ret2 = ListDel_2(L,i);
    if(ret){
        cout << "删除成功"<< endl;
    }else{
        cout << "删除失败"<< endl;
    }
    cout<<"删除后遍历这个表"<<endl;
    ListPrint(L);
    cout << "------------------------------" << endl;

    return 0;
}

运行结果:

双链表

双链表的核心操作与单链表基本无异,双链表多了一个前指针,指向节点的前一个节点,更方便了某些操作

(1)初始化赋值

初始化链表

bool InitDList(DPList& L){
    if(L!=NULL){
    L = (DNode* )malloc(sizeof(DPList));//开辟头节点
    L->prior = NULL;
    L->next = NULL;
    }else{
        return false;
    }
    return true;
}

尾插赋值

DPList SetDList(DPList& L){
    InitDList(L);//初始一个空表 
    int t;
    DNode* r;//创建尾指针
    r = L;
    cout <<"请输入节点数:";
    cin>>t;
    cout <<"请输入数据"<<endl;
    for(int i=0;i<t;i++){
        int m;
        cin>>m;
        DNode* p = (DNode*)malloc(sizeof(DPList));
        p->data = m;//把数据给节点p
        r->next = p;//将r的next指向p
        p->prior =r;//将p的前指针指向r
        r = p;//尾指针指向下一个节点
    }
    r->next = NULL;
    return L;
}

遍历链表

void printDList(DPList t){
    DNode* s;
    s = t->next;
    while (s != NULL)
    {
        cout <<s->data<<" ";
        s = s->next;
        /* code */
    }
}
(2)插入

跟单链表操作差不多,在单链表先写了查找的函数,这里就直接写按位查找的插入操作了

bool DListInsert(DPList& L,int i,int e){
    int m = DListLen(L);//计算链表长度
    if(i<0 || i>m){
        return false;//判断传入位置是否合适
    }
    DNode* p;//显示p当前指向的节点
    int j = 0;//计数
    p = L;//p从头节点开始,头节点是第0个数据
    while(p!=NULL && j<i-1){//这里是i-1,因为后插操作使用的是前节点的next指针操作的
        p = p->next;
        j++;
    }
    //-------------------------------------以上是按位查找操作
    if(p==NULL){
        return false;
    }
    DNode* s = (DNode*)malloc(sizeof(DPList));//开辟一个节点用于插入
    s->data = e;//把数据给s节点
    p->next->prior = s;//p下一个节点的前指针指向s
    s->next = p->next;//将p下一个节点给s下一个节点
    s->prior = p;//s的前指针指向p
    p->next = s; //p的下一个节点改为s
    return true;
    //-------------------------------------后插操作
}

上面两个操作可以跟单链表里一下分别用两个函数实现,便于代码的维护和复用

(3)删除

删除指点节点

//按位删除
bool DListDel(DPList& L,int i){
    int m = DListLen(L);//计算链表长度
    if(i<0 || i>m){
        return false;//判断传入位置是否合适
    }
    DNode* p;//显示p当前指向的节点
    int j = 0;//计数
    p = L;//p从头节点开始,头节点是第0个数据
    while(p!=NULL && j<i-1){//这里是i-1,因为后插操作使用的是前节点的next指针操作的
        p = p->next;
        j++;
    }
    //-------------------------------------以上是按位查找操作
    if(p==NULL){
        return false;
    }
    DNode* s = p->next;//找到p的下一个节点
    if(s == NULL){
        return false;//如果下一个节点是NULL返回false
    }
    p->next = s->next;//把s下一个节点给p
    if(s->next!=NULL){//判断s下一个节点是否是空
        s->next->prior = p;//将s下一个节点的前指针指向p
    }
    free(s);//删除s
    return true;
    //-------------------------------------后插操作
}

完整代码

#include <iostream>
using namespace std;
typedef struct Node
{
    int data;
    struct Node* prior;//前节点
    struct Node* next;//后节点
    /* data */
}DNode,*DPList;
//初始化双链表
bool InitDList(DPList& L){
    if(L!=NULL){
    L = (DNode* )malloc(sizeof(DPList));//开辟头节点
    L->prior = NULL;
    L->next = NULL;
    }else{
        return false;
    }
    return true;
}
//建立双链表
DPList SetDList(DPList& L){
    InitDList(L);//初始一个空表 
    int t;
    DNode* r;//创建尾指针
    r = L;
    cout <<"请输入节点数:";
    cin>>t;
    cout <<"请输入数据"<<endl;
    for(int i=0;i<t;i++){
        int m;
        cin>>m;
        DNode* p = (DNode*)malloc(sizeof(DPList));
        p->data = m;//把数据给节点p
        r->next = p;//将r的next指向p
        p->prior =r;//将p的前指针指向r
        r = p;//尾指针指向下一个节点
    }
    r->next = NULL;
    return L;
}
//遍历链表
void printDList(DPList t){
    DNode* s;
    s = t->next;
    while (s != NULL)
    {
        cout <<s->data<<" ";
        s = s->next;
        /* code */
    }
}
//计算链表长度
int DListLen(DPList L){
    int num = 0;
    if(L==NULL){
        return num;
    }else{
        DNode* s;
        s = L;
        while (s->next!=NULL)
        {
            s = s->next;
            num++;
        } 
    }
    return num;
}
//按位插入
bool DListInsert(DPList& L,int i,int e){
    int m = DListLen(L);//计算链表长度
    if(i<0 || i>m){
        return false;//判断传入位置是否合适
    }
    DNode* p;//显示p当前指向的节点
    int j = 0;//计数
    p = L;//p从头节点开始,头节点是第0个数据
    while(p!=NULL && j<i-1){//这里是i-1,因为后插操作使用的是前节点的next指针操作的
        p = p->next;
        j++;
    }
    //-------------------------------------以上是按位查找操作
    if(p==NULL){
        return false;
    }
    DNode* s = (DNode*)malloc(sizeof(DPList));//开辟一个节点用于插入
    s->data = e;//把数据给s节点
    p->next->prior = s;//p下一个节点的前指针指向s
    s->next = p->next;//将p下一个节点给s下一个节点
    s->prior = p;//s的前指针指向p
    p->next = s; //p的下一个节点改为s
    return true;
    //-------------------------------------后插操作
}
//按位删除
bool DListDel(DPList& L,int i){
    int m = DListLen(L);//计算链表长度
    if(i<0 || i>m){
        return false;//判断传入位置是否合适
    }
    DNode* p;//显示p当前指向的节点
    int j = 0;//计数
    p = L;//p从头节点开始,头节点是第0个数据
    while(p!=NULL && j<i-1){//这里是i-1,因为后插操作使用的是前节点的next指针操作的
        p = p->next;
        j++;
    }
    //-------------------------------------以上是按位查找操作
    if(p==NULL){
        return false;
    }
    DNode* s = p->next;//找到p的下一个节点
    if(s == NULL){
        return false;//如果下一个节点是NULL返回false
    }
    p->next = s->next;//把s下一个节点给p
    if(s->next!=NULL){//判断s下一个节点是否是空
        s->next->prior = p;//将s下一个节点的前指针指向p
    }
    free(s);//删除s
    return true;
    //-------------------------------------后插操作
}
int main(){
    DPList L;
    L = SetDList(L);
    cout<<"----------------------------"<<endl;
    cout<<"插入前"<<endl;
    printDList(L);
    cout <<endl;
    cout<<"----------------------------"<<endl;
    cout <<"请分别输入插入的位置和数据:";
    int i;
    int e;
    cin>>i;
    cin>>e;
    DListInsert(L,i,e);
    cout<<"插入后"<<endl;
    printDList(L);
    cout <<endl;
    cout<<"----------------------------"<<endl;
    cout <<"请输入删除的位置:";
    cin>>i;
    DListDel(L,i);
    cout<<"删除后"<<endl;
    printDList(L);
    cout <<endl;
    cout<<"----------------------------"<<endl;
    return 0;
}

循环链表

循环链表有循环单链表和循环双链表,本质都是一样的,不过循环链表中最后一个节点的指向不在是NULL而是头节点,这也就意味着在循环链表操作中不存在NULL的判断

静态链表

静态链表实质上就是由数组实现的链表

原理图

数据被存放在一块连续的空间内,定义方式

#define MaxSize 10
typedef struct{
	int data;
	int next;
}SList[MaxSize];

这里的next保存的是下一个数据所在的位置

二、栈与队列

1.栈

(1)栈的顺序储存

功能实现

定义顺序栈

typedef struct{
  int data[MaxSize];//静态数组存放元素
  int top;//栈顶指针
}SStack;

初始化栈

SStack InitSStack(){
    SStack SS;
    SS.top=-1;//栈顶指针指向-1
    return SS;
}

判断栈是否为空

bool If_empty(SStack SS){
    return SS.top==-1;
}

判断是否满

bool If_full(SStack SS){
    return SS.top==MaxSize-1;
}

进栈

bool Push(SStack& SS,int x){
    if(SS.top==MaxSize-1){
        cout <<"入栈失败"<<endl;
        return false;
    }
    SS.data[++SS.top] = x;
    return true;
}

出栈

int pop(SStack& SS){
    if(If_empty(SS)){
        cout<<"出栈失败"<<endl;
        return false;
    }
    int item;
    item  = SS.data[SS.top];
    SS.top--;
    return item;
}
完整代码
#include <iostream>
using namespace std;
#define MaxSize 10
//定义顺序栈
typedef struct{
    int data[MaxSize];//静态数组存放元素
    int top;//栈顶指针
}SStack;

//初始化栈
SStack InitSStack(){
    SStack SS;
    SS.top=-1;//栈顶指针指向-1
    return SS;
}
//判断栈是否为空
bool If_empty(SStack SS){
    return SS.top==-1;
}
//判断栈是否已满
bool If_full(SStack SS){
    return SS.top==MaxSize-1;
}
//进栈
bool Push(SStack& SS,int x){
    if(SS.top==MaxSize-1){
        cout <<"入栈失败"<<endl;
        return false;
    }
    SS.data[++SS.top] = x;
    return true;
}
//出栈并返回值
int pop(SStack& SS){
    if(If_empty(SS)){
        cout<<"出栈失败"<<endl;
        return false;
    }
    int item;
    item  = SS.data[SS.top];
    SS.top--;
    return item;
}
int main(){
    SStack SS=InitSStack();
    for(int i=0;i<10;i++){
        Push(SS,i);
    }
    int item;
    for(int i=0;i<10;i++){
        item=pop(SS);
        cout <<item<<" ";
    }
    return 0;
}

(2)栈的链式储存

功能实现

定义链式节点

typedef struct ListNode
{
    int data;
    ListNode* next;
}ListNode;

定义链式栈类

class ListStack{
private:
    ListNode* top;
    int size;//队列长度
public:
    ListStack();//构造函数初始化队列
    void push(int x);//入队操作
    bool If_empty();//判断队列是否为空
    bool pop();//出队操作
    int GetTop();//获取栈顶元素
    void print(ListStack L);//通过出队和获取栈顶元素实现遍历
};

函数实现

ListStack::ListStack(){
    top = (ListNode*)malloc(sizeof(ListNode));
    //为top开辟空间,不开辟会报错
    top->next = NULL;//不带头节点
    size = 0;
}

void ListStack::push(int x){
    ListNode* p = (ListNode*)malloc(sizeof(ListNode));//开辟一个节点
    p->data =x;
    p->next = top->next;
    top->next = p;
    size++;
}

bool ListStack::If_empty(){
    return top->next==NULL;
}

bool ListStack::pop(){
    if(ListStack::If_empty()){
        return false;//队空返回false
    }
    ListNode* s = top->next;
    top->next = s->next;
    free(s);
    size--;
    return true;
}

int ListStack::GetTop(){
    return top->next->data;
}

void ListStack::print(ListStack L){
    int t = L.size;
    int item;
    for(int i=0;i<t;i++){
        item = L.GetTop();
        cout <<item<<" ";
        L.pop();
    }
}
完整代码
#include <iostream>
using namespace std;
//定义链式节点
typedef struct ListNode
{
    int data;
    ListNode* next;
}ListNode;
//定义链式队列类
class ListStack{
private:
    ListNode* top;
    int size;//队列长度
public:
    ListStack();//构造函数初始化队列
    void push(int x);//入队操作
    bool If_empty();//判断队列是否为空
    bool pop();//出队操作
    int GetTop();//获取栈顶元素
    void print(ListStack L);//通过出队和获取栈顶元素实现遍历
};
ListStack::ListStack(){
    top = (ListNode*)malloc(sizeof(ListNode));
    //为top开辟空间,不开辟会报错
    top->next = NULL;//不带头节点
    size = 0;
}

void ListStack::push(int x){
    ListNode* p = (ListNode*)malloc(sizeof(ListNode));//开辟一个节点
    p->data =x;
    p->next = top->next;
    top->next = p;
    size++;
}

bool ListStack::If_empty(){
    return top->next==NULL;
}

bool ListStack::pop(){
    if(ListStack::If_empty()){
        return false;//队空返回false
    }
    ListNode* s = top->next;
    top->next = s->next;
    free(s);
    size--;
    return true;
}

int ListStack::GetTop(){
    return top->next->data;
}

void ListStack::print(ListStack L){
    int t = L.size;
    int item;
    for(int i=0;i<t;i++){
        item = L.GetTop();
        cout <<item<<" ";
        L.pop();
    }
}
int main(){
    ListStack L;
    L.push(1);
    L.push(2);
    L.push(3);
    L.push(4);
    L.push(5);
    L.print(L);
    return 0;
}

(3)栈在括号匹配中的应用

在程序中的括号匹配可以利用栈来实现

算法实现
bool BraceMatch(char* str,int len){
    SStack SS = InitSStack();//初始化栈
    for(int i=0;i<len;i++){
        if(str[i]=='(' || str[i]=='[' || str[i]=='{'){
            Push(SS,str[i]);//扫描到左括号入栈
        }else{
            if(If_empty(SS))
                return false;//如果没有扫描到左括号并且栈为空返回false
            //这个时候左括号扫描完了,可以出栈左括号了
            char item = pop(SS);//用item接受出栈的值进行匹配
            if(str[i]==')' && item!='('){
                return false;//括号不匹配
            }
            if(str[i]==']' && item!='['){
                return false;//括号不匹配
            }
            if(str[i]=='}' && item!='{'){
                return false;//括号不匹配
            }
        }
    }
    return If_empty(SS);//如果全部成功出栈返回true
}
完整代码
#include <iostream>
using namespace std;
#define MaxSize 10
//定义顺序栈
typedef struct{
    char data[MaxSize];//静态数组存放元素
    int top;//栈顶指针
}SStack;

//初始化栈
SStack InitSStack(){
    SStack SS;
    SS.top=-1;//栈顶指针指向-1
    return SS;
}
//判断栈是否为空
bool If_empty(SStack SS){
    return SS.top==-1;
}
//判断栈是否已满
bool If_full(SStack SS){
    return SS.top==MaxSize-1;
}
//进栈
bool Push(SStack& SS,char x){
    if(SS.top==MaxSize-1){
        cout <<"入栈失败"<<endl;
        return false;
    }
    SS.data[++SS.top] = x;
    return true;
}
//出栈并返回值
char pop(SStack& SS){
    if(If_empty(SS)){
        cout<<"出栈失败"<<endl;
        return false;
    }
    char item;
    item  = SS.data[SS.top];
    SS.top--;
    return item;
}

bool BraceMatch(char* str,int len){
    SStack SS = InitSStack();//初始化栈
    for(int i=0;i<len;i++){
        if(str[i]=='(' || str[i]=='[' || str[i]=='{'){
            Push(SS,str[i]);//扫描到左括号入栈
        }else{
            if(If_empty(SS))
                return false;//如果没有扫描到左括号并且栈为空返回false
            //这个时候左括号扫描完了,可以出栈左括号了
            char item = pop(SS);//用item接受出栈的值进行匹配
            if(str[i]==')' && item!='('){
                return false;//括号不匹配
            }
            if(str[i]==']' && item!='['){
                return false;//括号不匹配
            }
            if(str[i]=='}' && item!='{'){
                return false;//括号不匹配
            }
        }
    }
    return If_empty(SS);//如果全部成功出栈返回true
}

int main(){
    char* str;
    int len = 6;
    for(int i = 0;i<len;i++){
        cin>>str[i];
    }
    if(BraceMatch(str,len)){
        cout <<"匹配成功";
    }else{
        cout <<"匹配失败";
    }
    return 0;
}

(4)栈在表达式求值中的应用

计算中缀表达式

算法思路

功能实现
void ListStack::compute(ListStack L1,ListStack L2){
    char a;//运算符栈传入的是运算的ascll码对应的,用char接收自动转化
    T b,c,d;
    a = L1.pop();
    b = L2.pop();
    c = L2.pop();
    if(a=='*'){
        d = c*b;//这里的c b位置不能改变,因为出栈的顺序是固定的
    }else if(a=='/'){
        d = c/b;
    }else if(a=='+'){
        d = c+b;
    }else if(a=='-'){
        d = c-d;
    }else{
        //cout <<"erro"<<endl;
    }
    L2.push(d);//将运算后的值重新压入栈中
}
//计算中缀表达式
//思路初始化两个栈,一个用来放操作数,一个用来放运算符
//要是遇到了操作数压入操作数栈
//要是遇到运算符压入运算符栈(弹出运算的时候也会弹出两个操作数进行计算,并将结果放回操作数栈)用compute函数实现
double ListStack::Cal(char str[100]){
    int len = 0;
    for(int i = 0;str[i]!='\0';i++){
        len++;//计算表达式长度
    }   
    ListStack L1;
    ListStack L2;
    //(1+2)*3-(2+2)/4
    for(int i =0;i<=len;i++){
        if('0'<=str[i] && str[i]<='9'){//判断是不是数字
            L2.push(str[i]-48);//因为char传入时转成ascll,减去48就是原来的值
            continue;//存入一个后直接开始下一轮循环
        }//这是当表达式的开头不是括号的时候   
        if(str[i]=='(' || L1.If_empty()){//如果开头是括号就将(压入栈,如果不是,那么执行了上面的if,下一个也肯定是运算符
            L1.push(str[i]);//这里不是存数字直接存就可以了
            continue;
        }
        if(str[i]==')'){//如果是)则要开始计算括号里的,)是不需要入栈的,只是一个判断的作用
            char item = L1.GetTop();
            //这个地方我开始写的时候是while(item!='('),这样就写成一个死循环了,因为现在的item就是开始的值静态的,这样写循环里要加一句item = L1.GetTop();重新获取写的栈顶值
            while(item!='('){//如果栈里不是两个连续的( (,这里用while不用if是因为可能括号里不止一个运算符  列入((1+2*2/2)*3)-(2+2)/4
                this->compute(L1,L2);//计算
                item = L1.GetTop();
            }
            L1.pop();//将栈中的(出栈
            continue;
        }
        if(str[i]=='*'||str[i]=='/'){
            T item2 = L1.GetTop();//看上一个运算符是否已经是*或/,如果是则先运算上一个* /  例如((1+2*2/2)*3)-(2+2)/4
            if(item2 == '/' || item2 == '*'){
                this->compute(L1,L2);
            }
            L1.push(str[i]);//在把这个运算符压入栈
            continue;
        }
        if(str[i]=='+' || str[i]=='-'){
            T item3 = L1.GetTop();
            if(item3 !='('){//如果栈最上面不是(则直接运算  列如  (1+(2-3)+4)/4
                this->compute(L1,L2);
            }
            L1.push(str[i]);//如果是则直接进栈
            continue;
        }
        while(!L1.If_empty()){//如果开头不是(,运算到最后栈不为空则继续运算
            this->compute(L1,L2); //例如1+2-(1*2)
        }
    }
    T num;
    num = L2.GetTop();//获取运算值
    return num;
}

测试代码

void test(){
    ListStack L;
    char str[100];
    cout <<"请输入计算的中缀式:";
    cin>>str;
    int num = L.Cal(str);
    cout <<num; 
}

2.队列

(1)队列的顺序储存

功能实现

队列的定义

typedef struct{
    int data[MaxSize];//用静态数组存放队列,一块连续的的储存空间
    int front,rear;//队头指针和队尾指针,队尾指针是指向下一个要插入元素的位置,而不是最后一个元素
    int size;//记录队列长度
}SQueue;

初始化一个队列

SQueue InitSQueue(){
    SQueue SQ;
    SQ.front=SQ.rear=0;//开始的时候队头和队尾指针都指向,起始位置
    SQ.size=0;
    return SQ;
}

判断队列是否为空

bool If_empty(SQueue SQ){
    if(SQ.front==SQ.rear&&SQ.size==0){//当两个指针指向同一块位置时判断为空且长度为0
        return true;
    }else{
        return false;
    }
}

判断队列是否已满

//判断队列是否已满
bool If_full(SQueue SQ){
    if(SQ.size==MaxSize){
        return true;
    }else{
        return false;
    }
}

入队操作

bool In_queue(SQueue& SQ,int e){
    bool ret = If_full(SQ);
    if(ret){
        cout<<"这个队列已满,入队失败"<<endl;
        return false;
    }
    SQ.data[SQ.rear]=e;//将数据入队
    SQ.rear=(SQ.rear+1)%MaxSize;//实现循环队列
    SQ.size++;//队列长度加1
    return true;
}

SQ.rear=(SQ.rear+1)%MaxSize;这一句要模上Maxsize之后当rear达到10的时候就会回到0,实现循环队列

出队操作

int Out_queue(SQueue& SQ,int& x){
    if(If_empty(SQ)){
        cout <<"出队失败"<<endl;//判读队列是否为空
        return 0;
    }
    x=SQ.data[SQ.front];
    SQ.front=(SQ.front+1)%MaxSize;
    SQ.size--;
    return x;
}

因为队列不能够遍历,所以用出队的返回值可以模拟遍历

完整代码
//顺序存储实现的队列
#include <iostream>
using namespace std;
#define MaxSize 10
//队列的定义
typedef struct{
    int data[MaxSize];//用静态数组存放队列,一块连续的的储存空间
    int front,rear;//队头指针和队尾指针,队尾指针是指向下一个要插入元素的位置,而不是最后一个元素
    int size;//记录队列长度
}SQueue;

//初始化一个顺序队列
SQueue InitSQueue(){
    SQueue SQ;
    SQ.front=SQ.rear=0;//开始的时候队头和队尾指针都指向,起始位置
    SQ.size=0;
    return SQ;
}
//判断队列是否为空
bool If_empty(SQueue SQ){
    if(SQ.front==SQ.rear&&SQ.size==0){//当两个指针指向同一块位置时判断为空
        return true;
    }else{
        return false;
    }
}
//判断队列是否已满
bool If_full(SQueue SQ){
    if(SQ.size==MaxSize){
        return true;
    }else{
        return false;
    }
}
//入队操作
bool In_queue(SQueue& SQ,int e){
    bool ret = If_full(SQ);
    if(ret){
        cout<<"这个队列已满,入队失败"<<endl;
        return false;
    }
    SQ.data[SQ.rear]=e;//将数据入队
    SQ.rear=(SQ.rear+1)%MaxSize;//实现循环队列
    SQ.size++;//队列长度加1
    return true;
}
//出队操作,并返回出队的值
int Out_queue(SQueue& SQ,int& x){
    if(If_empty(SQ)){
        cout <<"出队失败"<<endl;//判读队列是否为空
        return 0;
    }
    x=SQ.data[SQ.front];
    SQ.front=(SQ.front+1)%MaxSize;
    SQ.size--;
    return x;
}
int main(){
    int m;
    SQueue SQ = InitSQueue();
    for(int i = 0;i<10;i++){
        In_queue(SQ,i);
    }
    for(int i = 0;i<10;i++){
        Out_queue(SQ,m);
        cout <<m<<" ";
    }
    return 0;
}

(2)队列的链式储存

功能实现

队列定义

typedef struct ListNode
{
    int data;
    ListNode* next;
}ListNode;

队列类建立

class ListQueue{
private:
    ListNode* front;//队头指针
    ListNode* rear;//队尾指针
    int size;//队列长度
public:
    ListQueue();//构造函数初始化队列
    ~ListQueue();//析构函数销毁队列
    void In_Queue(int x);//入队操作
    bool If_empty();//判断队列是否为空
    bool Out_Queue(int& x);//出队操作
};

函数实现

ListQueue::ListQueue(){
    front = rear =(ListNode*)malloc(sizeof(ListNode));//队头和队尾指针都指向头节点
    front->next=NULL;
    size=0;
}

void ListQueue::In_Queue(int x){
    ListNode* s = (ListNode*)malloc(sizeof(ListNode));
    s->data = x;
    s->next =NULL;
    rear->next =s;//让rear的next指针域指向s
    rear = s;//将s改为尾指针
    size++;
}

bool ListQueue::If_empty(){
    if(this->front==this->rear){
        cout <<"这个队列是空的"<<endl;
        return true;
    }else{
        return false;
    }
}

bool ListQueue::Out_Queue(int& x){
    if(this->If_empty()){
        cout <<"出队失败"<<endl;
        return false;
    }
    ListNode* p = front->next;//用p指向这次要出队的节点
    x  = p->data;//用x接收要出队节点的数据
    front->next = p->next;//修改头节点指向
    if(rear == p){//如果要删除的是最后一个节点
        front = rear;
    }
    free(p);//删除节点
    size--;
    return true;
}

ListQueue::~ListQueue(){
    if(front!=rear){
        ListNode* p = front->next;//用p指向这次要出队的节点
        front->next = p->next;//修改头节点指向
        if(rear == p){//如果要删除的是最后一个节点
            front = rear;
        }
        free(p);
    }
}
完整代码
#include <iostream>
using namespace std;
//定义链式节点
typedef struct ListNode
{
    int data;
    ListNode* next;
}ListNode;
//定义链式队列类
class ListQueue{
private:
    ListNode* front;//队头指针
    ListNode* rear;//队尾指针
    int size;//队列长度
public:
    ListQueue();//构造函数初始化队列
    ~ListQueue();//析构函数销毁队列
    void In_Queue(int x);//入队操作
    bool If_empty();//判断队列是否为空
    bool Out_Queue(int& x);//出队操作
};

ListQueue::ListQueue(){
    front = rear =(ListNode*)malloc(sizeof(ListNode));//队头和队尾指针都指向头节点
    front->next=NULL;
    size=0;
}

void ListQueue::In_Queue(int x){
    ListNode* s = (ListNode*)malloc(sizeof(ListNode));
    s->data = x;
    s->next =NULL;
    rear->next =s;//让rear的next指针域指向s
    rear = s;//将s改为尾指针
    size++;
}

bool ListQueue::If_empty(){
    if(this->front==this->rear){
        cout <<"这个队列是空的"<<endl;
        return true;
    }else{
        return false;
    }
}

bool ListQueue::Out_Queue(int& x){
    if(this->If_empty()){
        cout <<"出队失败"<<endl;
        return false;
    }
    ListNode* p = front->next;//用p指向这次要出队的节点
    x  = p->data;//用x接收要出队节点的数据
    front->next = p->next;//修改头节点指向
    if(rear == p){//如果要删除的是最后一个节点
        front = rear;
    }
    free(p);//删除节点
    size--;
    return true;
}

ListQueue::~ListQueue(){
    if(front!=rear){
        ListNode* p = front->next;//用p指向这次要出队的节点
        front->next = p->next;//修改头节点指向
        if(rear == p){//如果要删除的是最后一个节点
            front = rear;
        }
        free(p);
    }
}
int main(){
    ListQueue L;
    for(int i =0;i<10;i++){
        L.In_Queue(i);
    }
    int m;
    for(int i =0;i<5;i++){
        L.Out_Queue(m);
        cout <<m<<" ";
    }
}

三、串

串,即字符串(String),是由零个或多个字符组成的有限序列。格式
其中,s是串名,引号(单/双引号都可)括起来的字符序列是串的值;a_{i}可以是字母、数字或其他字符;字符下标从1开始,串中字符的个数n称为串的长度。n=0时的串称为空串(用∅表示)。

基本操作实现就不做了,这里主要是两种匹配算法

朴素匹配算法

//朴素匹配算法
#include <iostream>
using namespace std;

int test(){
    char a[10],b[10];
    cin>>a;
    cin>>b;
    int i =0;int j=0;
    while(a[i]!='\0' && b[j]!='\0'){
        if(a[i]==b[j]){
            i++;
            j++;
        }else{
            i=i-j+1;
            j=0;
        }
    }
    if(b[j]=='\0'){//如果子串指向了\0,返回匹配的位置
        return i-j;
    }else{//如果没有找到,指向的是主串的\0运行else
        return -1;
    }
}
int main(){
    int m=test();
    if(m==-1){
        cout <<"匹配失败";
    }else{
        cout <<"匹配的位置是:"<<m;
    }
    return 0;
}

算是暴力求解

KMP算法


四、树

主要讨论二叉树

顺序储存

//顺序存储结构
#include<iostream>
using namespace std;
#define Max 100
#define ElemType int
typedef ElemType BiTree[Max];//定义二叉树

class Mytree{
private:
    BiTree tree;
    int size = 0;
public:
    Mytree(){}
    void InitBiTree();//初始化一个树
    void printBiTree();//遍历树(层次遍历)
    ElemType FindParent(ElemType e);//查找父节点
    ElemType LeftChild(ElemType e);//查找左孩子
    ElemType RightChild(ElemType e);//查找有孩子
    ElemType Gettree(ElemType e);//获取节点值
    bool EmptyTree();//判断是否是空树
};

void Mytree::InitBiTree(){
    ElemType node;
    cout<<"请按层次从左向右输入节点,空节点用0表示,按1010停止输入"<<endl;
    int i = 1;
    while(cin>>node){
        if(node == 1010){
            break;
        }
        tree[i] = node;
        i++;
        size++;
    }

}
void Mytree::printBiTree(){
    //层次遍历
    for(int i =1;i<=size;i++){
        cout<<tree[i]<<" ";
    }
    cout<<endl;
}
ElemType Mytree::FindParent(ElemType e){
    if(EmptyTree()) cout<<"这是个空树"<<endl;
    if(e == 1) cout<<"根节点没有父节点"<<endl;
    if(tree[e] == 0) { cout<<"不存在这个节点"; return -1;}
    for(int i =2;i<=size;i++){
        if(i == e){
            return tree[i/2];
        }
    }
    return -1;
}

ElemType Mytree::LeftChild(ElemType e){
    if(EmptyTree()) cout<<"这是个空树"<<endl;
    if(tree[e] == 0) { cout<<"不存在这个节点"; return -1;}
    for(int i =2;i<=size;i++){
        if(i == e){
            if(i*2<size && tree[i*2]!=0){
                return tree[i*2];
            }else{
                cout<<"当前节点没有左孩子"<<endl;
            }
        }
    }
    return -1;
}   

ElemType Mytree::RightChild(ElemType e){
    if(EmptyTree()) cout<<"这是个空树"<<endl;
    if(tree[e] == 0) { cout<<"不存在这个节点"; return -1;}
    for(int i =2;i<=size;i++){
        if(i == e){
            if(i*2+1<size && tree[i*2+1]!=0){
                return tree[i*2+1];
            }else{
                cout<<"当前节点没有右孩子"<<endl;
            }
        }
    }
    return -1;
}
bool Mytree::EmptyTree(){
    if(size == 0){
        return true;
    }else{
        return false;
    }
}

ElemType Mytree::Gettree(ElemType e){
    return tree[e];
}
int main(){
    Mytree a;
    int tmp;
    a.InitBiTree();
    a.printBiTree();
    cout<<"-----------------"<<endl;
    cout<<"请输入想要查询的节点"<<endl;
    cin>>tmp;
    cout<<"这个节点的的值是:"<<a.Gettree(tmp)<<endl;
    cout<<"这个节点的父节点:"<<a.FindParent(tmp)<<endl;
    cout<<"这个节点的左孩子:"<<a.LeftChild(tmp)<<endl;
    cout<<"这个节点的右孩子:"<<a.RightChild(tmp)<<endl;
    return 0;
}

这种方式适合存储完全二叉树,不然的话会浪费大量空间

链式存储

#include<iostream>
using namespace std;
#define ElemType int
typedef struct BiTreeNode//定义结构体
{
    ElemType  data; //数据域
    BiTreeNode *lChild;//左孩子
    BiTreeNode *rChlid;//右孩子
} BiTreeNode, *BiTree;
//先序创建二叉树
void CreateBiTree(BiTree &T){
    ElemType ch;

    cin >> ch;

    if (ch == -1)
        T = NULL;
    else
    {
        T = new BiTreeNode;
        T->data = ch;
        CreateBiTree(T->lChild);
        CreateBiTree(T->rChlid);
    }
}
//中序遍历
void GetMid(BiTree T){
    if(T){
        GetMid(T->lChild);
        cout<<T->data<<" ";
        GetMid(T->rChlid);
    }
}
//先序遍历
void GetFrist(BiTree T){
    if(T){
        cout<<T->data<<" ";
        GetMid(T->lChild);
        GetMid(T->rChlid);
    }
}
//后序遍历
void GetLast(BiTree T){
    if(T){
        GetMid(T->lChild);
        GetMid(T->rChlid);
        cout<<T->data<<" ";
    }
}
int main(void)
{
    BiTree T;
    cout<<"请输入先序遍历顺序下各个结点的值,-1表示没有结点:"<<endl;
    CreateBiTree(T);
    GetMid(T);
    cout<<endl;
    cout<<"---------------"<<endl;
    GetFrist(T);
    cout<<endl;
    cout<<"---------------"<<endl;
    GetLast(T);
    return 0;
}

二叉树的链式存储递归用的比较多

哈夫曼树

#include <iostream>
#include <algorithm>
#include <iomanip>
using namespace std;

typedef struct HuFuNode
{
	int weight;
	int parent, LTree, RTree;
}HuFuNode, *PHuFuNode;

void Select(PHuFuNode HuFuNode, int n, int &s1, int &s2)    
{
    for(int i = 1; i < n; i++){         
        if(HuFuNode[i].parent == 0){     
            s1 = i;					
            break;                          
        }
    }

    for(int i = 1; i < n; i++){         
            if(HuFuNode[i].parent == 0 && HuFuNode[s1].weight > HuFuNode[i].weight)
                s1 = i;
    }

    for(int j = 1; j < n; j++){        
        if(HuFuNode[j].parent == 0 && j != s1){
            s2 = j;
            break;
        }
    }

    for(int j = 1; j < n; j++){         
        if(HuFuNode[j].parent == 0 && HuFuNode[s2].weight > HuFuNode[j].weight && j != s1)
            s2 = j;
    }
}

void initHuFuNode(PHuFuNode &H, int n)
{
	if(n <= 1) return;
	int m = 2*n - 1;            //数组共2n - 1个元素
	H = new HuFuNode[m + 1];	//0号单元未用,H[m]表示根节点
    for(int i = 1; i <= m; i++){
        H[i].parent = 0;
        H[i].LTree = 0;
        H[i].RTree = 0;
    }
	for(int i = 1; i <= n; i++)
        cin >> H[i].weight;
    cout << endl;
    for(int i = n + 1; i <= m; i++)         
    {
        int s1, s2;
        Select(H, i, s1, s2);
        H[s1].parent = i;
        H[s2].parent = i;   
        H[i].LTree = s1;
        H[i].RTree = s2;
        H[i].weight = H[s1].weight + H[s2].weight;
    }
    for(int i =1 ;i<=m;i++){
        cout<<H[i].weight<<" ";
    }
}

int main()
{
    PHuFuNode HuFuNode;
    int n = 0;
    cin >> n;
    initHuFuNode(HuFuNode, n);
	return 0;
}

哈夫曼编码

#include<bits/stdc++.h>
#define Elemtype int
#define max 100
using namespace std;

//哈曼树定义
typedef struct HuffCode{
    Elemtype weight;//权重
    int lchild;
    int rchild;
    int parent;//定义左右孩子,双亲节点
}codeNode,Huffmancode[max];


typedef char** cd;
//第一个参数是传入数组,第二个值是数组遍历的终点值,用s1,s2返回两个最小值
void select(Huffmancode HC,int n,int& s1,int& s2){
    for(int i = 1;i<n;i++){
        if(HC[i].parent==0){
            s1 = i;
            break;
        }
    }

    for(int i = 1;i<n;i++){
        if(HC[i].parent==0 && HC[s1].weight>HC[i].weight){
            s1 = i;
        }
    }
    for(int j = 1;j<n;j++){
        if(HC[j].parent==0 && j!=s1){
            s2 = j;
            break;
        }
    }

    for(int j = 1;j<n;j++){
        if(HC[j].parent==0 && HC[s2].weight>HC[j].weight && j!=s1){
            s2 = j;
        }
    }
}

//哈夫曼树的构建
void Creat_Huff(Huffmancode& HC,int n){//n为节点数量
    int m = 2*n-1;//需要m大小的数组存数据
    int i,s1,s2;//s1,s2用来返回最小的两个值
    for(i=1;i<=n;i++){//初始化
        HC[i].parent = 0;
        HC[i].lchild = 0;
        HC[i].rchild = 0;
        cin>>HC[i].weight;
    }
    //处理后面的节点
    for(i=n+1;i<=m;i++){
        HC[i].parent = 0;
        HC[i].lchild = 0;
        HC[i].rchild = 0;
        HC[i].weight = 0;
    }
    //选出数组中最小的两个树,构建哈夫曼树
    for(i=n+1;i<=m;i++){
        select(HC,i,s1,s2);
        HC[i].weight = HC[s1].weight+HC[s2].weight;
        HC[i].lchild = s1;
        HC[i].rchild = s2;
        HC[s1].parent = i;
        HC[s2].parent = i;
    }
    cout<<"n"<<"     "<<"w"<<"      "<<"p"<<"      "<<"l"<<"       "<<"r"<<endl;
    for(int i = 1;i<=m;i++){
        cout<<i<<"     "<<HC[i].weight<<"      "<<HC[i].parent<<"     "<<HC[i].lchild<<"      "<<HC[i].rchild<<endl;
        cout<<left;
    }

}

//----------------------------------------------------以上是哈夫曼树的实现在上一节已经实现

//哈夫曼编码实现
void Creat_code(Huffmancode& HC,cd& code,int n){
    code = new char*[n+1]; //分配n个字符编码的头指针矢量
    char* a = new char[n];//分配临时变量用来存编码
    a[n-1] = '\0';//编码结束符
    for(int i =1;i<=n;i++){//逐个字符求哈夫曼编码
        int start = n-1;//开始存放的位置,因为n-1位放的是\0,起始设置在\0
        int c = i;//要找的节点c
        int f = HC[i].parent;//设置双亲节点的初始值
        while(f!=0){//从叶子节点开始回溯,寻找双亲节点为0的位置,也就是根节点
            --start;//初始strar指向\0
            if(HC[f].lchild == c){
                a[start]='0';//双亲节点的左孩子为c则设置为0
            }else{
                a[start]='1';
            }
            c = f;//将这个双亲节点变为孩子节点
            f = HC[f].parent;//下一个要找的双亲节点位置
        }
        code[i] = new char [n-start];
        strcpy(code[i],&a[start]);
    }
    delete a;
}


int main(){
    Huffmancode HC;//创建哈夫曼树
    int n;
    cin>>n;
    cd code;
    Creat_Huff(HC,n);
    Creat_code(HC,code, n);
    for(int i =1;i<=n;i++){
        cout<<code[i]<<" ";
    }
    return 0;
}

五、图

邻接矩阵存储

实现步骤

  1. 输入节点数,和边数
  2. 输入节点信息,存入一个数组中
  3. 初始化邻接矩阵,如果是图初始化为0,如果是网,初始化无穷大
  4. 依次输入每条边依附的两个节点,如果有权值,还要输入权值
#include<bits/stdc++.h>
/* 1. 输入节点数,和边数
2. 输入节点信息,存入一个数组中
3. 初始化邻接矩阵,如果是图初始化为0,如果是网,初始化无穷大
4. 依次输入每条边依附的两个节点,如果有权值,还要输入权值 */
//两个数组一个用来存顶点表,一个用来存邻接矩阵
using namespace std;
#define min 0 //初始化值
#define MAXNum 100
typedef char vre;//顶点的数据类型
typedef int arc;//边的权值

typedef struct{
    vre v[MAXNum];//创建顶点表
    arc a[MAXNum][MAXNum];//创建邻接矩阵
    int spot,side;//顶点数和边数
}AMGraph;

int LocateVex(AMGraph G,int v1){
    for(int i =0;i<G.spot;i++){
        if(G.v[i] == v1){
            return i;
        }
    }
    return 0;
}
//创建无向网
void Creat_UDN(AMGraph& G){
    char v1,v2;
    int w;
    cout<<"分别输入节点和边的数量"<<endl;
    cin>>G.spot>>G.side;//输入边和点
    cout<<"输入节点信息"<<endl;
    for(int i =0;i<G.spot;i++){
        cin>>G.v[i];//输入点的信息
    }
    for(int i =0;i<G.spot;i++){
        for(int j =0;j<G.spot;j++){
            G.a[i][j] = min;//设置边的权值
        }
    }//初始化边

    cout<<"输入每条边依附的节点以及权值"<<endl;
    for(int k =0;k<G.side;k++){    
        cin>>v1>>v2>>w;
        int i = LocateVex(G,v1);
        int j = LocateVex(G,v2);
        G.a[i][j] = w;
        //cout<<G.a[i][j]<<" ";
        G.a[j][i] = G.a[i][j]  ;
        //cout<<G.a[j][i]<<endl;
    }
    cout<<"图的邻接矩阵为:"<<endl;
    for (int i = 0; i < G.spot; i++)
    {
        for(int j=0;j<G.spot;j++){
            cout<<G.a[i][j]<<" ";
        }
        cout<<endl;
    }
    
}

int main(){
    AMGraph G;
    Creat_UDN(G);
    return 0;
}

邻接表存储


文章作者: zhang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zhang !
  目录