天天看點

算法訓練營入門 代碼 自用

5.3  二叉樹周遊

traverse

#include <iostream>
#include <queue>//引入隊列頭檔案
using namespace std;

typedef struct Bnode	/*定義二叉樹存儲結構*/
{ char data;
  struct Bnode *lchild,*rchild;
}Bnode,*Btree;

void Createtree(Btree &T)	/*建立二叉樹函數*/
{
    //按先序次序輸入二叉樹中結點的值(一個字元),建立二叉連結清單表示的二叉樹T
	char ch;
	cin >> ch;
	if(ch=='#')
        T=NULL;			//遞歸結束,建空樹
	else{
		T=new Bnode;
		T->data=ch;					//生成根結點
		Createtree(T->lchild);	//遞歸建立左子樹
		Createtree(T->rchild);	//遞歸建立右子樹
	}
}

void preorder(Btree T)//先序周遊
{
    if(T)
    {
       cout<<T->data<<"  ";
       preorder(T->lchild);
       preorder(T->rchild);
    }
}

void inorder(Btree T)//中序周遊
{
    if(T)
    {
       inorder(T->lchild);
       cout<<T->data<<"  ";
       inorder(T->rchild);
    }
}

void posorder(Btree T)//後序周遊
{
    if(T)
    {
       posorder(T->lchild);
       posorder(T->rchild);
       cout<<T->data<<"  ";
    }
}

bool Leveltraverse(Btree T)
{
    Btree p;
    if(!T)
        return false;
    queue<Btree>Q; //建立一個普通隊列(先進先出),裡面存放指針類型
    Q.push(T); //根指針入隊
    while(!Q.empty()) //如果隊列不空
    {
        p=Q.front();//取出隊頭元素作為目前擴充結點livenode
        Q.pop(); //隊頭元素出隊
        cout<<p->data<<"  ";
        if(p->lchild)
            Q.push(p->lchild); //左孩子指針入隊
        if(p->rchild)
            Q.push(p->rchild); //右孩子指針入隊
    }
    return true;
}

int main()
{
    Btree mytree;
    cout<<"按先序次序輸入二叉樹中結點的值(孩子為空時輸入#),建立一棵二叉樹"<<endl;
    Createtree(mytree);//建立二叉樹
    cout<<endl;
    cout<<"二叉樹的先序周遊結果:"<<endl;
    preorder(mytree);//先序周遊二叉樹
    cout<<endl;
    cout<<"二叉樹的中序周遊結果:"<<endl;
    inorder(mytree);//中序周遊二叉樹
    cout<<endl;
    cout<<"二叉樹的後序周遊結果:"<<endl;
    posorder(mytree);//後序周遊二叉樹
    cout<<endl;
    cout<<"二叉樹的層次周遊結果:"<<endl;
    Leveltraverse(mytree);//層次周遊二叉樹
    return 0;
}
           

depth 

#include <iostream>
using namespace std;

typedef struct Bnode	/*定義二叉樹存儲結構*/
{ char data;
  struct Bnode *lchild,*rchild;
}Bnode,*Btree;

void Createtree(Btree &T)	/*建立二叉樹函數*/
{
    //按先序次序輸入二叉樹中結點的值(一個字元),建立二叉連結清單表示的二叉樹T
	char ch;
	cin >> ch;
	if(ch=='#')
        T=NULL;			//遞歸結束,建空樹
	else{
		T=new Bnode;
		T->data=ch;					//生成根結點
		Createtree(T->lchild);	//遞歸建立左子樹
		Createtree(T->rchild);	//遞歸建立右子樹
	}
}

int Depth(Btree T)//求二叉樹的深度
{
    int m,n;
    if(T==NULL)//如果為空樹,深度為0
        return 0;
    else
    {
       m=Depth(T->lchild);//遞歸計算左子樹深度
       n=Depth(T->rchild);//遞歸計算左子樹深度
       if(m>n)
          return m+1;//傳回左右子樹最大值加1
       else
          return n+1;
    }
}

int main()
{
    Btree mytree;
    cout<<"按先序次序輸入二叉樹中結點的值(孩子為空時輸入#),建立一棵二叉樹"<<endl;
    //ABD##E##CF#G###
    Createtree(mytree);//建立二叉樹
    cout<<endl;
    cout<<"二叉樹的深度為:"<<Depth(mytree)<<endl;
    return 0;
}
           

leaf 

#include <iostream>
using namespace std;

typedef struct Bnode	/*定義二叉樹存儲結構*/
{ char data;
  struct Bnode *lchild,*rchild;
}Bnode,*Btree;

void Createtree(Btree &T)	/*建立二叉樹函數*/
{
    //按先序次序輸入二叉樹中結點的值(一個字元),建立二叉連結清單表示的二叉樹T
	char ch;
	cin >> ch;
	if(ch=='#')
        T=NULL;			//遞歸結束,建空樹
	else{
		T=new Bnode;
		T->data=ch;					//生成根結點
		Createtree(T->lchild);	//遞歸建立左子樹
		Createtree(T->rchild);	//遞歸建立右子樹
	}
}

int LeafCount(Btree T)//求二叉樹的葉子數
{
    if(T==NULL)//如果為空樹,深度為0
        return 0;
    else
        if(T->lchild==NULL&&T->rchild==NULL)//左右子樹均為空,則葉子數為1
           return 1;
        else
           return LeafCount(T->lchild)+LeafCount(T->rchild);//遞歸計算左子樹和右子樹的葉子數之和
}

int NodeCount(Btree T)//求二叉樹的結點數
{
    if(T==NULL)//如果為空樹,深度為0
        return 0;
    else
        return NodeCount(T->lchild)+NodeCount(T->rchild)+1;//遞歸計算左子樹和右子樹的結點數之和加1
}

int main()
{
    Btree mytree;
    cout<<"按先序次序輸入二叉樹中結點的值(孩子為空時輸入#),建立一棵二叉樹"<<endl;
    //ABD##E##CF#G###
    Createtree(mytree);//建立二叉樹
    cout<<endl;
    cout<<"二叉樹的結點數為:"<<NodeCount(mytree)<<endl;
    cout<<"二叉樹的葉子數為:"<<LeafCount(mytree)<<endl;
    return 0;
}
           

PreinCreateBitree 

#include <iostream>
using namespace std;
typedef struct node
{
    char data;
    struct node *lchild,*rchild;
}BiTNode,*BiTree;

BiTree pre_mid_createBiTree(char *pre,char *mid,int len) //前序中序還原建立二叉樹
{
    if(len==0)
        return NULL;
    char ch=pre[0];  //找到先序中的第一個結點
    int index=0;
    while(mid[index]!=ch)//在中序中找到的根結點的左邊為該結點的左子樹,右邊為右子樹
    {
        index++;
    }
    BiTree T=new BiTNode;//建立根結點
    T->data=ch;
    T->lchild=pre_mid_createBiTree(pre+1,mid,index);//建立左子樹
    T->rchild=pre_mid_createBiTree(pre+index+1,mid+index+1,len-index-1);//建立右子樹
    return T;
}

BiTree pro_mid_createBiTree(char *last,char *mid,int len)//後序中序還原建立二叉樹
{
    if(len==0)
       return NULL;
    char ch=last[len-1]; //取得後序周遊順序中最後一個結點
    int index=0;//在中序序列中找根結點,并用index記錄長度
    while(mid[index]!=ch)//在中序中找到根結點,左邊為該結點的左子樹,右邊為右子樹
       index++;
    BiTree T=new BiTNode;//建立根結點
    T->data=ch;
    T->lchild=pro_mid_createBiTree(last,mid,index);//建立左子樹
    T->rchild=pro_mid_createBiTree(last+index,mid+index+1,len-index-1);//建立右子樹
    return T;
}

void pre_order(BiTree T)//前序遞歸周遊二叉樹
{
    if(T)
    {
        cout<<T->data;
        pre_order(T->lchild);
        pre_order(T->rchild);
    }
}

void pro_order(BiTree T)//後序遞歸周遊二叉樹
{
    if(T)
    {
        pro_order(T->lchild);
        pro_order(T->rchild);
        cout<<T->data;
    }
}
int main()
{
    BiTree T;
    int n;
    char pre[100],mid[100],last[100];
    cout<<"1. 前序中序還原二叉樹\n";
	cout<<"2. 後序中序還原二叉樹\n";
	cout<<"0. 退出\n";
	int choose=-1;
	while(choose!=0)
	{
	    cout<<"請選擇:";
		cin>>choose;

		switch (choose)
		{
		    case 1://前序中序還原二叉樹
		        cout<<"請輸入結點的個數:"<<endl;
		        cin>>n;
		        cout<<"請輸入前序序列:"<<endl;
		        for(int i=0;i<n;i++)
                    cin>>pre[i];
                cout<<"請輸入中序序列:"<<endl;
                for(int i=0;i<n;i++)
                    cin>>mid[i];
                T=pre_mid_createBiTree(pre,mid,n);
                cout<<endl;
                cout<<"二叉樹還原成功,輸出其後序序列:"<<endl;
                pro_order(T);
                cout<<endl<<endl;
                break;
            case 2://後序中序還原二叉樹
                cout<<"請輸入結點的個數:"<<endl;
		        cin>>n;
                cout<<"請輸入後序序列:"<<endl;
                for(int i=0 ;i<n;i++)
                    cin>>last[i];
                cout<<"請輸入中序序列:"<<endl;
                for(int i=0 ;i<n;i++)
                    cin>>mid[i];
                T=pro_mid_createBiTree(last,mid,n);
                cout<<endl;
                cout<<"二叉樹還原成功,輸出其先序序列:"<<endl;
                pre_order(T);
                cout<<endl<<endl;
                break;
		}
	}
    return 0;
}
           

6.圖

6.1 圖的存儲1 建立無向圖的鄰接矩陣

#include<iostream>//建立無向圖的鄰接矩陣
using namespace std;
#define MaxVnum 100  //頂點數最大值
typedef char VexType;  //頂點的資料類型,根據需要定義
typedef int EdgeType;  //邊上權值的資料類型,若不帶權值的圖,則為0或1
typedef struct {
    VexType Vex[MaxVnum];
    EdgeType Edge[MaxVnum][MaxVnum];
    int vexnum, edgenum; //頂點數,邊數
}AMGraph;

int locatevex(AMGraph G, VexType x) {
    for (int i = 0; i < G.vexnum; i++)//查找頂點資訊的下标
        if (x == G.Vex[i])
            return i;
    return -1;//沒找到
}


void CreateAMGraph(AMGraph& G) {
    int i, j;
    VexType u, v;
    cout << "請輸入頂點數:" << endl;
    cin >> G.vexnum;
    cout << "請輸入邊數:" << endl;
    cin >> G.edgenum;
    cout << "請輸入頂點資訊:" << endl;
    for (int i = 0; i < G.vexnum; i++)//輸入頂點資訊,存入頂點資訊數組
        cin >> G.Vex[i];
    for (int i = 0; i < G.vexnum; i++)//初始化鄰接矩陣所有值為0,如果是網,則初始化鄰接矩陣為無窮大
        for (int j = 0; j < G.vexnum; j++)
            G.Edge[i][j] = 0;
    cout << "請輸入每條邊依附的兩個頂點:" << endl;
    while (G.edgenum--) {
        cin >> u >> v;
        i = locatevex(G, u);//查找頂點u的存儲下标
        j = locatevex(G, v);//查找頂點v的存儲下标
        if (i != -1 && j != -1)
            G.Edge[i][j] = G.Edge[j][i] = 1; //鄰接矩陣儲置1
        else {
            cout << "輸入頂點資訊錯!請重新輸入!" << endl;
            G.edgenum++;//本次輸入不算
        }
    }
}

void print(AMGraph G) {//輸出鄰接矩陣
    cout << "圖的鄰接矩陣為:" << endl;
    for (int i = 0; i < G.vexnum; i++) {
        for (int j = 0; j < G.vexnum; j++)
            cout << G.Edge[i][j] << "\t";
        cout << endl;
    }
}

int main() {
    AMGraph G;
    CreateAMGraph(G);
    print(G);
    return 0;
}
/*
4 5
a b c d
a b
a d
b c
b d
c d
*/

           

6.1 圖的存儲2 建立有向圖的鄰接表

#include<iostream>//建立有向圖的鄰接表
using namespace std;
const int MaxVnum = 100;//頂點數最大值
typedef char VexType;//頂點的資料類型為字元型

typedef struct AdjNode { //定義鄰接點類型
    int v; //鄰接點下标
    struct AdjNode* next; //指向下一個鄰接點
}AdjNode;

typedef struct VexNode { //定義頂點類型
    VexType data; // VexType為頂點的資料類型,根據需要定義
    AdjNode* first; //指向第一個鄰接點
}VexNode;

typedef struct {//定義鄰接表類型
    VexNode Vex[MaxVnum];
    int vexnum, edgenum; //頂點數,邊數
}ALGraph;

int locatevex(ALGraph G, VexType x) {
    for (int i = 0; i < G.vexnum; i++)//查找頂點資訊的下标
        if (x == G.Vex[i].data)
            return i;
    return -1;//沒找到
}

void insertedge(ALGraph& G, int i, int j) {//插入一條邊
    AdjNode* s;
    s = new AdjNode;
    s->v = j;
    s->next = G.Vex[i].first;
    G.Vex[i].first = s;
}

void printg(ALGraph G) {//輸出鄰接表
    cout << "----------鄰接表如下:----------" << endl;
    for (int i = 0; i < G.vexnum; i++) {
        AdjNode* t = G.Vex[i].first;
        cout << G.Vex[i].data << ":  ";
        while (t != NULL) {
            cout << "[" << t->v << "]\t";
            t = t->next;
        }
        cout << endl;
    }
}

void CreateALGraph(ALGraph& G) {//建立有向圖鄰接表
    int i, j;
    VexType u, v;
    cout << "請輸入頂點數和邊數:" << endl;
    cin >> G.vexnum >> G.edgenum;
    cout << "請輸入頂點資訊:" << endl;
    for (i = 0; i < G.vexnum; i++)//輸入頂點資訊,存入頂點資訊數組
        cin >> G.Vex[i].data;
    for (i = 0; i < G.vexnum; i++)
        G.Vex[i].first = NULL;
    cout << "請依次輸入每條邊的兩個頂點u,v" << endl;
    while (G.edgenum--) {
        cin >> u >> v;
        i = locatevex(G, u);//查找頂點u的存儲下标
        j = locatevex(G, v);//查找頂點v的存儲下标
        if (i != -1 && j != -1)
            insertedge(G, i, j);
        else {
            cout << "輸入頂點資訊錯!請重新輸入!" << endl;
            G.edgenum++;//本次輸入不算
        }
    }
}

int main() {
    ALGraph G;
    CreateALGraph(G);//建立有向圖鄰接表
    printg(G);//輸出鄰接表
    return 0;
}
/*
5 7
a b c d e
a b
a c
a e
b c
c d
c e
d e
*/
           

6.1 圖的存儲3 建立無向網的鍊式向前星 

#include<iostream>//建立無向網的鍊式前向星 
#include<cstring>
using namespace std;
const int maxn = 100000 + 5;
int maxx[maxn], head[maxn];
int n, m, x, y, w, cnt;

struct Edge {
	int to, w, next;
}e[maxn];

void add(int u, int v, int w) {//添加一條邊u--v 
	e[cnt].to = v;
	e[cnt].w = w;
	e[cnt].next = head[u];
	head[u] = cnt++;
}

void printg() {//輸對外連結式前向星
	cout << "----------鍊式前向星如下:----------" << endl;
	for (int v = 1; v <= n; v++) {
		cout << v << ":  ";
		for (int i = head[v]; ~i; i = e[i].next) {
			int v1 = e[i].to, w1 = e[i].w;
			cout << "[" << v1 << " " << w1 << "]\t";
		}
		cout << endl;
	}
}

int main() {
	cin >> n >> m;
	memset(head, -1, sizeof(head));
	cnt = 0;
	for (int i = 1; i <= m; i++) {
		cin >> x >> y >> w;
		add(x, y, w);//添加邊
		add(y, x, w);//添加反向邊 
	}
	printg();
	return 0;
}
/*
4 5
1 2 5
1 4 3
2 3 8
2 4 12
3 4 9
*/
           

6.2周遊1 基于鄰接矩陣的廣度優先周遊 

#include<iostream>
#include<queue>//引入隊列頭檔案
using namespace std;
const int MaxVnum = 100;//頂點數最大值
bool visited[MaxVnum];  //通路标志數組,其初值為"false"
typedef char VexType;  //頂點的資料類型,根據需要定義
typedef int EdgeType;  //邊上權值的資料類型,若不帶權值的圖,則為0或1
typedef struct {
    VexType Vex[MaxVnum];
    EdgeType Edge[MaxVnum][MaxVnum];
    int vexnum, edgenum; //頂點數,邊數
}AMGraph;

int locatevex(AMGraph G, VexType x) {
    for (int i = 0; i < G.vexnum; i++)//查找頂點資訊的下标
        if (x == G.Vex[i])
            return i;
    return -1;//沒找到
}

void CreateAMGraph(AMGraph& G) {//建立有向圖的鄰接矩陣
    int i, j;
    VexType u, v;
    cout << "請輸入頂點數:" << endl;
    cin >> G.vexnum;
    cout << "請輸入邊數:" << endl;
    cin >> G.edgenum;
    cout << "請輸入頂點資訊:" << endl;
    for (int i = 0; i < G.vexnum; i++)//輸入頂點資訊,存入頂點資訊數組
        cin >> G.Vex[i];
    for (int i = 0; i < G.vexnum; i++)//初始化鄰接矩陣所有值為0,如果是網,則初始化鄰接矩陣為無窮大
        for (int j = 0; j < G.vexnum; j++)
            G.Edge[i][j] = 0;
    cout << "請輸入每條邊依附的兩個頂點:" << endl;
    while (G.edgenum--) {
        cin >> u >> v;
        i = locatevex(G, u);//查找頂點u的存儲下标
        j = locatevex(G, v);//查找頂點v的存儲下标
        if (i != -1 && j != -1)
            G.Edge[i][j] = 1; //鄰接矩陣儲置1,若無向圖G.Edge[i][j]=G.Edge[j][i]=1
        else {
            cout << "輸入頂點資訊錯!請重新輸入!" << endl;
            G.edgenum++;//本次輸入不算
        }
    }
}

void print(AMGraph G) {//輸出鄰接矩陣
    cout << "圖的鄰接矩陣為:" << endl;
    for (int i = 0; i < G.vexnum; i++) {
        for (int j = 0; j < G.vexnum; j++)
            cout << G.Edge[i][j] << "\t";
        cout << endl;
    }
}

void BFS_AM(AMGraph G, int v) {//基于鄰接矩陣的廣度優先周遊
    int u, w;
    queue<int>Q; //建立一個普通隊列(先進先出),裡面存放int類型
    cout << G.Vex[v] << "\t";
    visited[v] = true;
    Q.push(v); //源點v入隊
    while (!Q.empty()) { //如果隊列不空
        u = Q.front();//取出隊頭元素指派給u
        Q.pop(); //隊頭元素出隊
        for (w = 0; w < G.vexnum; w++) {//依次檢查u的所有鄰接點
            if (G.Edge[u][w] && !visited[w]) {//u、w鄰接而且w未被通路
                cout << G.Vex[w] << "\t";
                visited[w] = true;
                Q.push(w);
            }
        }
    }
}

int main() {
    int v;
    VexType c;
    AMGraph G;
    CreateAMGraph(G);
    print(G);
    cout << "請輸入周遊圖的起始點:";
    cin >> c;
    v = locatevex(G, c);//查找頂點u的存儲下标
    if (v != -1) {
        cout << "廣度優先搜尋周遊圖結果:" << endl;
        BFS_AM(G, v);
    }
    else
        cout << "輸入頂點資訊錯!請重新輸入!" << endl;
    return 0;
}
/*測試資料
6 9
1 2 3 4 5 6
1 3
1 2
2 4
3 5
3 2
4 6
4 3
5 6
5 4
1
*/
           

6.2周遊2 基于鄰接表的廣度優先周遊

#include<iostream>
#include<queue>//引入隊列頭檔案
using namespace std;
const int MaxVnum = 100;//頂點數最大值
bool visited[MaxVnum];  //通路标志數組,其初值為"false"
typedef char VexType;//頂點的資料類型為字元型

typedef struct AdjNode { //定義鄰接點類型
    int v; //鄰接點下标
    struct AdjNode* next; //指向下一個鄰接點
}AdjNode;

typedef struct VexNode { //定義頂點類型
    VexType data; // VexType為頂點的資料類型,根據需要定義
    AdjNode* first; //指向第一個鄰接點
}VexNode;

typedef struct {//定義鄰接表類型
    VexNode  Vex[MaxVnum];
    int vexnum, edgenum; //頂點數,邊數
}ALGraph;

int locatevex(ALGraph G, VexType x) {
    for (int i = 0; i < G.vexnum; i++)//查找頂點資訊的下标
        if (x == G.Vex[i].data)
            return i;
    return -1;//沒找到
}

void insertedge(ALGraph& G, int i, int j) {//插入一條邊
    AdjNode* s;
    s = new AdjNode;
    s->v = j;
    s->next = G.Vex[i].first;
    G.Vex[i].first = s;
}

void printg(ALGraph G) {//輸出鄰接表
    cout << "----------鄰接表如下:----------" << endl;
    for (int i = 0; i < G.vexnum; i++) {
        AdjNode* t = G.Vex[i].first;
        cout << G.Vex[i].data << ":  ";
        while (t != NULL) {
            cout << "[" << t->v << "]  ";
            t = t->next;
        }
        cout << endl;
    }
}

void CreateALGraph(ALGraph& G) {//建立有向圖鄰接表
    int i, j;
    VexType u, v;
    cout << "請輸入頂點數和邊數:" << endl;
    cin >> G.vexnum >> G.edgenum;
    cout << "請輸入頂點資訊:" << endl;
    for (i = 0; i < G.vexnum; i++)//輸入頂點資訊,存入頂點資訊數組
        cin >> G.Vex[i].data;
    for (i = 0; i < G.vexnum; i++)
        G.Vex[i].first = NULL;
    cout << "請依次輸入每條邊的兩個頂點u,v" << endl;
    while (G.edgenum--) {
        cin >> u >> v;
        i = locatevex(G, u);//查找頂點u的存儲下标
        j = locatevex(G, v);//查找頂點v的存儲下标
        if (i != -1 && j != -1)
            insertedge(G, i, j);
        else {
            cout << "輸入頂點資訊錯!請重新輸入!" << endl;
            G.edgenum++;//本次輸入不算
        }
    }
}

void BFS_AL(ALGraph G, int v) {//基于鄰接表的廣度優先周遊
    int u, w;
    AdjNode* p;
    queue<int>Q; //建立一個普通隊列(先進先出),裡面存放int類型
    cout << G.Vex[v].data << "\t";
    visited[v] = true;
    Q.push(v); //源點v入隊
    while (!Q.empty()) { //如果隊列不空
        u = Q.front();//取出隊頭元素指派給u
        Q.pop(); //隊頭元素出隊
        p = G.Vex[u].first;
        while (p) {//依次檢查u的所有鄰接點
            w = p->v;//w為u的鄰接點
            if (!visited[w]) {//w未被通路
                cout << G.Vex[w].data << "\t";
                visited[w] = true;
                Q.push(w);
            }
            p = p->next;
        }
    }
}

void BFS_AL(ALGraph G) {//非連通圖,基于鄰接表的廣度優先周遊
    for (int i = 0; i < G.vexnum; i++)//非連通圖需要查漏點,檢查未被通路的頂點
        if (!visited[i])//i未被通路,以i為起點再次廣度優先周遊
            BFS_AL(G, i);
}

int main() {
    ALGraph G;
    int v;
    VexType c;
    CreateALGraph(G);//建立有向圖鄰接表
    printg(G);//輸出鄰接表
    cout << "請輸入周遊圖的起始點:";
    cin >> c;
    v = locatevex(G, c);//查找頂點u的存儲下标
    if (v != -1) {
        cout << "廣度優先搜尋周遊圖結果:" << endl;
        BFS_AL(G, v);
    }
    else
        cout << "輸入頂點資訊錯!請重新輸入!" << endl;
    return 0;
}
/*測試資料
6 9
1 2 3 4 5 6
1 3
1 2
2 4
3 5
3 2
4 6
4 3
5 6
5 4
1
*/
           

6.2周遊3 基于鄰接矩陣的深度優先周遊

#include<iostream>
using namespace std;
const int MaxVnum = 100;     //頂點數最大值
bool visited[MaxVnum];  //通路标志數組,其初值為"false"
typedef char VexType;  //頂點的資料類型,根據需要定義
typedef int EdgeType;  //邊上權值的資料類型,若不帶權值的圖,則為0或1
typedef struct {
    VexType Vex[MaxVnum];
    EdgeType Edge[MaxVnum][MaxVnum];
    int vexnum, edgenum; //頂點數,邊數
}AMGraph;

int locatevex(AMGraph G, VexType x) {
    for (int i = 0; i < G.vexnum; i++)//查找頂點資訊的下标
        if (x == G.Vex[i])
            return i;
    return -1;//沒找到
}

void CreateAMGraph(AMGraph& G) {//建立無向圖的鄰接矩陣
    int i, j;
    VexType u, v;
    cout << "請輸入頂點數:" << endl;
    cin >> G.vexnum;
    cout << "請輸入邊數:" << endl;
    cin >> G.edgenum;
    cout << "請輸入頂點資訊:" << endl;
    for (int i = 0; i < G.vexnum; i++)//輸入頂點資訊,存入頂點資訊數組
        cin >> G.Vex[i];
    for (int i = 0; i < G.vexnum; i++)//初始化鄰接矩陣所有值為0,如果是網,則初始化鄰接矩陣為無窮大
        for (int j = 0; j < G.vexnum; j++)
            G.Edge[i][j] = 0;
    cout << "請輸入每條邊依附的兩個頂點:" << endl;
    while (G.edgenum--) {
        cin >> u >> v;
        i = locatevex(G, u);//查找頂點u的存儲下标
        j = locatevex(G, v);//查找頂點v的存儲下标
        if (i != -1 && j != -1)
            G.Edge[i][j] = G.Edge[j][i] = 1; //若有向圖G.Edge[i][j]=1
        else {
            cout << "輸入頂點資訊錯!請重新輸入!" << endl;
            G.edgenum++;//本次輸入不算
        }
    }
}

void print(AMGraph G) {//輸出鄰接矩陣
    cout << "圖的鄰接矩陣為:" << endl;
    for (int i = 0; i < G.vexnum; i++) {
        for (int j = 0; j < G.vexnum; j++)
            cout << G.Edge[i][j] << "\t";
        cout << endl;
    }
}

void DFS_AM(AMGraph G, int v) {//基于鄰接矩陣的深度優先周遊
    int w;
    cout << G.Vex[v] << "\t";
    visited[v] = true;
    for (w = 0; w < G.vexnum; w++)//依次檢查v的所有鄰接點
        if (G.Edge[v][w] && !visited[w])//v、w鄰接而且w未被通路
            DFS_AM(G, w);//從w頂點開始遞歸深度優先周遊
}

int main() {
    int v;
    VexType c;
    AMGraph G;
    CreateAMGraph(G);//建立無向圖的鄰接矩陣 
    print(G);
    cout << "請輸入周遊連通圖的起始點:";
    cin >> c;
    v = locatevex(G, c);//查找頂點u的存儲下标
    if (v != -1) {
        cout << "深度優先搜尋周遊連通圖結果:" << endl;
        DFS_AM(G, v);
    }
    else
        cout << "輸入頂點資訊錯!請重新輸入!" << endl;
    return 0;
}
/*測試資料
8 9
1 2 3 4 5 6 7 8
1 3
1 2
2 6
2 5
2 4
3 8
3 7
4 5
7 8
1
*/
           

6.2周遊4 基于鄰接表的深度優先周遊

#include<iostream>
using namespace std;
const int MaxVnum = 100;  //頂點數最大值
bool visited[MaxVnum];  //通路标志數組,其初值為"false"
typedef char VexType;   //頂點的資料類型為字元型
typedef struct AdjNode { //定義鄰接點類型
    int v; //鄰接點下标
    struct AdjNode* next; //指向下一個鄰接點
}AdjNode;

typedef struct VexNode { //定義頂點類型
    VexType data; // VexType為頂點的資料類型,根據需要定義
    AdjNode* first; //指向第一個鄰接點
}VexNode;

typedef struct {//定義鄰接表類型
    VexNode  Vex[MaxVnum];
    int vexnum, edgenum; //頂點數,邊數
}ALGraph;

int locatevex(ALGraph G, VexType x) {
    for (int i = 0; i < G.vexnum; i++)//查找頂點資訊的下标
        if (x == G.Vex[i].data)
            return i;
    return -1;//沒找到
}

void insertedge(ALGraph& G, int i, int j) {//插入一條邊
    AdjNode* s;
    s = new AdjNode;
    s->v = j;
    s->next = G.Vex[i].first;
    G.Vex[i].first = s;
}

void printg(ALGraph G) {//輸出鄰接表
    cout << "----------鄰接表如下:----------" << endl;
    for (int i = 0; i < G.vexnum; i++) {
        AdjNode* t = G.Vex[i].first;
        cout << G.Vex[i].data << ":  ";
        while (t != NULL) {
            cout << "[" << t->v << "]  ";
            t = t->next;
        }
        cout << endl;
    }
}

void CreateALGraph(ALGraph& G) {//建立無向圖鄰接表
    int i, j;
    VexType u, v;
    cout << "請輸入頂點數和邊數:" << endl;
    cin >> G.vexnum >> G.edgenum;
    cout << "請輸入頂點資訊:" << endl;
    for (i = 0; i < G.vexnum; i++)//輸入頂點資訊,存入頂點資訊數組
        cin >> G.Vex[i].data;
    for (i = 0; i < G.vexnum; i++)
        G.Vex[i].first = NULL;
    cout << "請依次輸入每條邊的兩個頂點u,v" << endl;
    while (G.edgenum--) {
        cin >> u >> v;
        i = locatevex(G, u);//查找頂點u的存儲下标
        j = locatevex(G, v);//查找頂點v的存儲下标
        if (i != -1 && j != -1) {
            insertedge(G, i, j);
            insertedge(G, j, i);//無向圖多插入一條邊
        }
        else {
            cout << "輸入頂點資訊錯!請重新輸入!" << endl;
            G.edgenum++;//本次輸入不算
        }
    }
}

void DFS_AL(ALGraph G, int v) {//基于鄰接表的深度優先周遊
    int w;
    AdjNode* p;
    cout << G.Vex[v].data << "\t";
    visited[v] = true;
    p = G.Vex[v].first;
    while (p) {//依次檢查v的所有鄰接點
        w = p->v;//w為v的鄰接點
        if (!visited[w])//w未被通路
            DFS_AL(G, w);//從w出發,遞歸深度優先周遊
        p = p->next;
    }
}

void DFS_AL(ALGraph G) {//非連通圖,基于鄰接表的深度優先周遊
    for (int i = 0; i < G.vexnum; i++)//非連通圖需要查漏點,檢查未被通路的頂點
        if (!visited[i])//i未被通路,以i為起點再次深度優先周遊
            DFS_AL(G, i);
}

int main() {
    ALGraph G;
    int v;
    VexType c;
    CreateALGraph(G);//建立無向圖的鄰接表
    printg(G);//輸出鄰接表
    cout << "請輸入周遊連通圖的起始點:";
    cin >> c;
    v = locatevex(G, c);//查找頂點u的存儲下标
    if (v != -1) {
        cout << "深度優先搜尋周遊連通圖結果:" << endl;
        DFS_AL(G, v);
    }
    else
        cout << "輸入頂點資訊錯!請重新輸入!" << endl;
    return 0;
}
/*測試資料
8 9
1 2 3 4 5 6 7 8
1 3
1 2
2 6
2 5
2 4
3 8
3 7
4 5
7 8
1
*/
           

7.1 最短路徑 

7.1 最短路徑1 dijkstra 

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int MaxVnum = 100; //城市的個數可修改
const int INF = 0x3f3f3f3f; //無窮大
int dist[MaxVnum], p[MaxVnum];//最短距離和前驅數組
bool flag[MaxVnum]; //如果s[i]等于true,說明頂點i已經加入到集合S;否則頂點i屬于集合V-S
typedef string VexType;  //頂點的資料類型,根據需要定義
typedef int EdgeType;  //邊上權值的資料類型,若不帶權值的圖,則為0或1
typedef struct {
	VexType Vex[MaxVnum];
	EdgeType Edge[MaxVnum][MaxVnum];
	int vexnum, edgenum; //頂點數,邊數
}AMGraph;

int locatevex(AMGraph G, VexType x) {
	for (int i = 0; i < G.vexnum; i++)//查找頂點資訊的下标
		if (x == G.Vex[i])
			return i;
	return -1;//沒找到
}

void CreateAMGraph(AMGraph& G) {
	int i, j, w;
	VexType u, v;
	cout << "請輸入頂點數:" << endl;
	cin >> G.vexnum;
	cout << "請輸入邊數:" << endl;
	cin >> G.edgenum;
	cout << "請輸入頂點資訊:" << endl;
	for (int i = 0; i < G.vexnum; i++)//輸入頂點資訊,存入頂點資訊數組
		cin >> G.Vex[i];
	for (int i = 0; i < G.vexnum; i++)//初始化鄰接矩陣為無窮大
		for (int j = 0; j < G.vexnum; j++)
			G.Edge[i][j] = INF;
	cout << "請輸入每條邊依附的兩個頂點及權值:" << endl;
	while (G.edgenum--) {
		cin >> u >> v >> w;
		i = locatevex(G, u);//查找頂點u的存儲下标
		j = locatevex(G, v);//查找頂點v的存儲下标
		if (i != -1 && j != -1)
			G.Edge[i][j] = w; //有向圖鄰接矩陣
		else {
			cout << "輸入頂點資訊錯!請重新輸入!" << endl;
			G.edgenum++;//本次輸入不算
		}
	}
}

void Dijkstra(AMGraph G, int u) {
	for (int i = 0; i < G.vexnum; i++) {
		dist[i] = G.Edge[u][i]; //初始化源點u到其他各個頂點的最短路徑長度
		flag[i] = false;
		if (dist[i] == INF)
			p[i] = -1; //源點u到該頂點的路徑長度為無窮大,說明頂點i與源點u不相鄰
		else
			p[i] = u; //說明頂點i與源點u相鄰,設定頂點i的前驅p[i]=u
	}
	dist[u] = 0;
	flag[u] = true;   //初始時,集合S中隻有一個元素:源點u
	for (int i = 0; i < G.vexnum; i++) {
		int temp = INF, t = u;
		for (int j = 0; j < G.vexnum; j++) //在集合V-S中尋找距離源點u最近的頂點t
			if (!flag[j] && dist[j] < temp) {
				t = j;
				temp = dist[j];
			}
		if (t == u) return; //找不到t,跳出循環
		flag[t] = true;  //否則,将t加入集合
		for (int j = 0; j < G.vexnum; j++)//更新V-S中與t相鄰接的頂點到源點u的距離
			if (!flag[j] && G.Edge[t][j] < INF)
				if (dist[j] > (dist[t] + G.Edge[t][j])) {
					dist[j] = dist[t] + G.Edge[t][j];
					p[j] = t;
				}
	}
}

void findpath(AMGraph G, VexType u) {
	int x;
	stack<int>S;
	cout << "源點為:" << u << endl;
	for (int i = 0; i < G.vexnum; i++) {
		x = p[i];
		if (x == -1 && u != G.Vex[i]) {
			cout << "源點到其它各頂點最短路徑為:" << u << "--" << G.Vex[i] << "    sorry,無路可達" << endl;
			continue;
		}
		while (x != -1) {
			S.push(x);
			x = p[x];
		}
		cout << "源點到其它各頂點最短路徑為:";
		while (!S.empty()) {
			cout << G.Vex[S.top()] << "--";
			S.pop();
		}
		cout << G.Vex[i] << "    最短距離為:" << dist[i] << endl;
	}
}

int main() {
	AMGraph G;
	int st;
	VexType u;
	CreateAMGraph(G);
	cout << "請輸入源點的資訊:" << endl;
	cin >> u;
	st = locatevex(G, u);//查找源點u的存儲下标
	Dijkstra(G, st);
	cout << "小明所在的位置:" << u << endl;
	for (int i = 0; i < G.vexnum; i++) {
		cout << "小明:" << u << " - " << "要去的位置:" << G.Vex[i];
		if (dist[i] == INF)
			cout << " sorry,無路可達" << endl;
		else
			cout << " 最短距離為:" << dist[i] << endl;
	}
	findpath(G, u);
	return 0;
}
/*
5 8
1 2 3 4 5
1 2 2
1 3 5
2 3 2
2 4 6
3 4 7
3 5 1
4 3 2
4 5 4
1
*/
           

7.1 最短路徑2 Floyd

#include<iostream>
#include<cstring>
using namespace std;
const int MaxVnum = 100; //頂點數最大值
const int INF = 0x3f3f3f3f; // 無窮大
typedef string VexType;  //頂點的資料類型,根據需要定義
typedef int EdgeType;  //邊上權值的資料類型,若不帶權值的圖,則為0或1
typedef struct {
    VexType Vex[MaxVnum];
    EdgeType Edge[MaxVnum][MaxVnum];
    int vexnum, edgenum; //頂點數,邊數
}AMGraph;
int dist[MaxVnum][MaxVnum], p[MaxVnum][MaxVnum];

int locatevex(AMGraph G, VexType x) {
    for (int i = 0; i < G.vexnum; i++)//查找頂點資訊的下标
        if (x == G.Vex[i])
            return i;
    return -1;//沒找到
}

void CreateAMGraph(AMGraph& G) {//建立無向圖的鄰接矩陣
    int i, j, w;
    VexType u, v;
    cout << "請輸入頂點數:" << endl;
    cin >> G.vexnum;
    cout << "請輸入邊數:" << endl;
    cin >> G.edgenum;
    cout << "請輸入頂點資訊:" << endl;
    for (int i = 0; i < G.vexnum; i++)//輸入頂點資訊,存入頂點資訊數組
        cin >> G.Vex[i];
    for (int i = 0; i < G.vexnum; i++)//初始化鄰接矩陣所有值為0,若是網,則初始化為無窮大
        for (int j = 0; j < G.vexnum; j++)
            if (i != j)
                G.Edge[i][j] = INF;
            else
                G.Edge[i][j] = 0; //注意i==j時,設定為0
    cout << "請輸入每條邊依附的兩個頂點及權值:" << endl;
    while (G.edgenum--) {
        cin >> u >> v >> w;
        i = locatevex(G, u);//查找頂點u的存儲下标
        j = locatevex(G, v);//查找頂點v的存儲下标
        if (i != -1 && j != -1)
            G.Edge[i][j] = w; //有向圖鄰接矩陣存儲權值
    }
}

void Floyd(AMGraph G) { //用Floyd算法求有向網G中各對頂點i和j之間的最短路徑
    int i, j, k;
    for (i = 0; i < G.vexnum; i++)          		//各對結點之間初始已知路徑及距離
        for (j = 0; j < G.vexnum; j++) {
            dist[i][j] = G.Edge[i][j];
            if (dist[i][j] < INF && i != j)
                p[i][j] = i;  	//如果i和j之間有弧,則将j的前驅置為i
            else p[i][j] = -1;  //如果i和j之間無弧,則将j的前驅置為-1
        }
    for (k = 0; k < G.vexnum; k++)
        for (i = 0; i < G.vexnum; i++)
            for (j = 0; j < G.vexnum; j++)
                if (dist[i][k] + dist[k][j] < dist[i][j]) {//從i經k到j的一條路徑更短
                    dist[i][j] = dist[i][k] + dist[k][j]; //更新dist[i][j]
                    p[i][j] = p[k][j];   //更改j的前驅
                }
}

void print(AMGraph G) {
    int i, j;
    for (i = 0; i < G.vexnum; i++) {//輸出最短距離數組
        for (j = 0; j < G.vexnum; j++)
            cout << dist[i][j] << "\t";
        cout << endl;
    }
    cout << endl;
    for (i = 0; i < G.vexnum; i++) {//輸出前驅數組
        for (j = 0; j < G.vexnum; j++)
            cout << p[i][j] << "\t";
        cout << endl;
    }
}

void DisplayPath(AMGraph G, int s, int t) {//顯示最短路徑
    if (p[s][t] != -1) {
        DisplayPath(G, s, p[s][t]);
        cout << G.Vex[p[s][t]] << "-->";
    }
}

int main() {
    VexType start, destination;
    int u, v;
    AMGraph G;
    CreateAMGraph(G);
    Floyd(G);
    print(G);
    cout << "請依次輸入路徑的起點與終點的名稱:";
    cin >> start >> destination;
    u = locatevex(G, start);
    v = locatevex(G, destination);
    DisplayPath(G, u, v);
    cout << G.Vex[v] << endl;
    cout << "最短路徑的長度為:" << dist[u][v] << endl;
    cout << endl;
    return 0;
}
/*
4 8
0 1 2 3
0 1 1
0 3 4
1 2 9
1 3 2
2 0 3
2 1 5
2 3 8
3 2 6
0 2
*/
           

7.1 最短路徑3 bellman ford

#include<iostream>
#include<cstring>
using namespace std;
struct node {
	int a, b, w;
}e[210];
int dis[110];
int n, m, cnt = 0;

void add(int a, int b, int w) {
	e[cnt].a = a;
	e[cnt].b = b;
	e[cnt++].w = w;
}

bool bellman_ford(int u) {//求源點u到其它頂點的最短路徑長度,判負環 
	memset(dis, 0x3f, sizeof(dis));
	dis[u] = 0;
	for (int i = 1; i < n; i++) {//執行n-1次
		bool flag = false;
		for (int j = 0; j < m; j++)//邊數m或cnt
			if (dis[e[j].b] > dis[e[j].a] + e[j].w) {
				dis[e[j].b] = dis[e[j].a] + e[j].w;
				flag = true;
			}
		if (!flag)
			return false;
	}
	for (int j = 0; j < m; j++)//再執行1次,還能松弛說明有環
		if (dis[e[j].b] > dis[e[j].a] + e[j].w)
			return true;
	return false;
}

void print() {//輸出源點到其它節點的最短距離 
	cout << "最短距離:" << endl;
	for (int i = 1; i <= n; i++)
		cout << dis[i] << " ";
	cout << endl;
}

int main() {
	int a, b, w;
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		cin >> a >> b >> w;
		add(a, b, w);
	}
	if (bellman_ford(1))//判斷負環
		cout << "有負環!" << endl;
	else
		print();
	return 0;
}
/*測試資料1
5 8
1 2 2
1 3 5
2 3 2
2 4 6
3 4 7
3 5 1
4 3 2
4 5 4
*/
/*測試資料2,有負環
4 4
1 2 3
2 3 -4
3 4 2
4 2 1
*/
           

7.1 最短路徑4 spfa

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 505, maxe = 100001;
int n, m, cnt;
int head[maxn], dis[maxn], sum[maxn];
bool vis[maxn];//标記是否在隊列中 
struct node {
	int to, next, w;
}e[maxe];

void add(int u, int v, int w) {
	e[cnt].to = v;
	e[cnt].next = head[u];
	e[cnt].w = w;
	head[u] = cnt++;
}

bool spfa(int u) {
	queue<int>q;
	memset(vis, 0, sizeof(vis));//标記是否在隊列中
	memset(sum, 0, sizeof(sum));//統計入隊的次數
	memset(dis, 0x3f, sizeof(dis));
	vis[u] = 1;
	dis[u] = 0;
	sum[u]++;
	q.push(u);
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		vis[x] = 0;
		for (int i = head[x]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] > dis[x] + e[i].w) {
				dis[v] = dis[x] + e[i].w;
				if (!vis[v]) {
					if (++sum[v] >= n)
						return true;
					vis[v] = 1;
					q.push(v);
				}
			}
		}
	}
	return false;
}

void print() {//輸出源點到其它節點的最短距離 
	cout << "最短距離:" << endl;
	for (int i = 1; i <= n; i++)
		cout << dis[i] << " ";
	cout << endl;
}

int main() {
	cnt = 0;
	cin >> n >> m;
	memset(head, -1, sizeof(head));
	int u, v, w;
	for (int i = 1; i <= m; i++) {
		cin >> u >> v >> w;
		add(u, v, w);
	}
	if (spfa(1))
		cout << "有負環!" << endl;
	else
		print();
	return 0;
}
/*測試資料1
5 8
1 2 2
1 3 5
2 3 2
2 4 6
3 4 7
3 5 1
4 3 2
4 5 4
*/
/*測試資料2,有負環
4 4
1 2 3
2 3 -4
3 4 2
4 2 1
*/
           

7.2  最小生成樹

Prim

#include<iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 100;
bool s[N];//如果s[i]=true,說明頂點i已加入U
int c[N][N], closest[N], lowcost[N];

void Prim(int n) {
    s[1] = true; //初始時,集合中U隻有一個元素,即頂點1
    for (int i = 1; i <= n; i++) {
        if (i != 1) {
            lowcost[i] = c[1][i];
            closest[i] = 1;
            s[i] = false;
        }
        else
            lowcost[i] = 0;
    }
    for (int i = 1; i < n; i++) {
        int temp = INF;
        int t = 1;
        for (int j = 1; j <= n; j++) {//在集合中V-u中尋找距離集合U最近的頂點t
            if (!s[j] && lowcost[j] < temp) {
                t = j;
                temp = lowcost[j];
            }
        }
        if (t == 1)
            break;//找不到t,跳出循環
        s[t] = true;//否則,t加入集合U
        for (int j = 1; j <= n; j++) { //更新lowcost和closest
            if (!s[j] && c[t][j] < lowcost[j]) {
                lowcost[j] = c[t][j];
                closest[j] = t;
            }
        }
    }
}

int main() {
    int n, m, u, v, w;
    cin >> n >> m;
    int sumcost = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            c[i][j] = INF;
    for (int i = 1; i <= m; i++) {
        cin >> u >> v >> w;
        c[u][v] = c[v][u] = w;
    }
    Prim(n);
    cout << "數組lowcost:" << endl;
    for (int i = 1; i <= n; i++)
        cout << lowcost[i] << " ";
    cout << endl;
    for (int i = 1; i <= n; i++)
        sumcost += lowcost[i];
    cout << "最小的花費:" << sumcost << endl;
    return 0;
}
/*測試資料
7 12
1 2 23
1 6 28
1 7 36
2 3 20
2 7 1
3 4 15
3 7 4
4 5 3
4 7 9
5 6 17
5 7 16
6 7 25

輸出結果:
數組lowcost:
0 23 4 9 3 17 1
最小的花費:57
*/
           

Kruskal

#include<iostream>
#include<algorithm>
using namespace std;
const int N=100;
int fa[N];
int n,m;
struct Edge{
    int u,v,w;
}e[N*N];

bool cmp(Edge x, Edge y){
    return x.w<y.w;
}

void Init(int n){//初始化集合号為自身 
    for(int i=1;i<=n;i++)
        fa[i]=i;
}

int Merge(int a,int b){//合并 
    int p=fa[a];
    int q=fa[b];
    if(p==q) return 0;
    for(int i=1;i<=n;i++){//檢查所有結點,把集合号是q的改為p
		if(fa[i]==q)
			fa[i]=p;//a的集合号指派給b集合号
    }
    return 1;
}

int Kruskal(int n){//求最小生成樹 
    int ans=0;
    sort(e,e+m,cmp);
    for(int i=0;i<m;i++)
        if(Merge(e[i].u,e[i].v)){
            ans+=e[i].w;
            n--;
            if(n==1)//n-1次合并算法結束 
            	return ans;
        }
    return 0;
}

int main(){
    cin>>n>>m;
    Init(n);
    for(int i=1;i<=m;i++)
        cin>>e[i].u>>e[i].v>>e[i].w;
    cout<<"最小的花費是:"<<Kruskal(n)<<endl;
    return 0;
}
/*測試資料 
7 12
1 2 23
1 6 28
1 7 36
2 3 20
2 7 1
3 4 15
3 7 4
4 5 3
4 7 9
5 6 17
5 7 16
6 7 25
*/
           

 Kruskal_2

#include<iostream>
#include<algorithm>
using namespace std;
const int N=100;
int fa[N];
int n,m;

struct Edge{
    int u,v,w;
}e[N*N];

bool cmp(Edge x,Edge y){
    return x.w<y.w;
}

void Init(int n){//初始化集合号為自身 
    for(int i=1;i<=n;i++)
        fa[i]=i;
}

int Find(int x){//找祖宗 
    if(x!=fa[x])
		fa[x]=Find(fa[x]);
    return fa[x];
}

bool Merge(int a,int b){
    int p=Find(a);
    int q=Find(b);
    if(p==q) return 0;
    fa[q]=p;
    return 1;
}

int Kruskal(int n){
    int ans=0;
    sort(e,e+m,cmp);
    for(int i=0;i<m;i++)
        if(Merge(e[i].u,e[i].v)){
            ans+=e[i].w;
            n--;
            if(n==1)
                return ans;
        }
    return 0;
}

int main(){
    cin>>n>>m;
    Init(n);
    for(int i=1;i<=m;i++)
        cin>>e[i].u>>e[i].v>>e[i].w;
    cout<<"最小的花費:"<<Kruskal(n)<<endl;
    return 0;
}
/*測試資料 
7 12
1 2 23
1 6 28
1 7 36
2 3 20
2 7 1
3 4 15
3 7 4
4 5 3
4 7 9
5 6 17
5 7 16
6 7 25
*/
           

7.3拓撲排序

Topo sort

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int maxn=105;
int map[maxn][maxn],indegree[maxn],topo[maxn];
int n,m;
stack<int>s;

bool TopoSort(){ //拓撲排序
	int cnt=0;
    for(int i=0;i<n;i++)
        if(indegree[i]==0)
        	s.push(i);
    while(!s.empty()){
		int u=s.top();
    	s.pop();
    	topo[cnt++]=u;
    	for(int j=0;j<n;j++)
            if(map[u][j])
            	if(--indegree[j]==0)
        			s.push(j);	
	}
	if(cnt<n) return 0;
	return 1;
}

int main(){
    cin>>n>>m;
    memset(map,0,sizeof(map));
    memset(indegree,0,sizeof(indegree));
	for(int i=0;i<m;i++){
    	int u,v;
    	cin>>u>>v;
    	map[u][v]=1;
    	indegree[v]++;
	}
	TopoSort();
	for(int i=0;i<n-1;i++)
		cout<<topo[i]<<" ";
	cout<<topo[n-1]<<endl;
    return 0;
}
/*測試資料
6 8
0 1
0 2
0 3
2 1
2 4
3 4
5 3
5 4
*/ 
           

 7.4 關鍵路徑

CriticalPath

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int maxn=10010,maxe=50010;
int n,m,cnt;
int head[maxn]; //鍊式前向星頭 
int in[maxn],topo[maxn]; //入度,拓撲序列 
int ve[maxn]; //事件vi的最早發生時間
int vl[maxn]; //事件vi的最遲發生時間
stack<int>s;
struct node{
	int to,next,w;
}e[maxe];

void add(int u,int v,int w){
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
	e[cnt++].w=w;
}

bool TopoSort(){//拓撲排序
	int cnt=0;
    for(int i=0;i<n;i++)
        if(in[i]==0)
        	s.push(i);
    while(!s.empty()){
		int u=s.top();
    	s.pop();
    	topo[cnt++]=u;
    	for(int i=head[u];~i;i=e[i].next){
    		int v=e[i].to;
            if(--in[v]==0)
        		s.push(v);
		}      
	}
	if(cnt<n) return 0;//該有向圖有回路
	return 1;
}

bool CriticalPath(){//關鍵路徑 
    if(TopoSort()){
        cout<<"拓撲序列為:"<<endl;
        for(int i=0;i<n;i++)//輸出拓撲序列
            cout<<topo[i]<<"\t";
        cout<<endl;
    }
    else{
    	cout<<"該圖有環,無拓撲序列!"<<endl;
    	return 0;
	} 
    for(int i=0;i<n;i++)//初始化最早發生時間為0
		ve[i]=0;
    //按拓撲次序求每個事件的最早發生時間
    for(int j=0;j<n;j++){
		int u=topo[j];  //取得拓撲序列中的頂點
		for(int i=head[u];~i;i=e[i].next){
    		int v=e[i].to,w=e[i].w;
            if(ve[v]<ve[u]+w)
        		ve[v]=ve[u]+w;
		} 
    }
    for(int i=0;i<n;i++) //初始化每個事件的最遲發生時間為ve[n]
		vl[i]=ve[n-1];
    for(int j=n-1;j>=0;j--){//按逆拓撲序求每個事件的最遲發生時間
		int u=topo[j];  //取得拓撲序列中的頂點
		for(int i=head[u];~i;i=e[i].next){
    		int v=e[i].to,w=e[i].w;
            if(vl[u]>vl[v]-w)
        		vl[u]=vl[v]-w;
		}
    }
    cout<<"事件的最早發生時間和最遲發生時間:"<<endl;
	for(int i=0;i<n;i++)
       cout<<ve[i]<<"\t"<<vl[i]<<endl;
	cout<<"關鍵活動路徑為:"<<endl;
    for(int u=0;u<n;u++){ //每次循環針對vi為活動開始點的所有活動
		for(int i=head[u];~i;i=e[i].next){
    		int v=e[i].to,w=e[i].w;
            int e=ve[u];    //計算活動<vi, vj>的最早開始時間e
			int l=vl[v]-w; //計算活動<vi, vj>的最遲開始時間l
			if(e==l)   //若為關鍵活動,則輸出<vi, vj>
				cout<<"<"<<u<<","<<v<<">"<<endl;
		}
	}
	return 1;
}

int main(){
    int u,v,w;
	cin>>n>>m;
	memset(head,-1,sizeof(head));
    memset(in,0,sizeof(in));
    for(int i=0;i<m;i++){
        cin>>u>>v>>w;
        add(u,v,w);
        in[v]++;
    }
    CriticalPath();
    return 0;
}
/*測試資料 
6 8
0 1 2
0 2 15
1 3 10
1 4 19
2 1 4
2 4 11
3 5 6
4 5 5
*/

           

8.1 哈希表 

Hash

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define m 15//哈希表的表長
#define NULLKEY 0//單元為空的标記
int HT[m],HC[m];

int H(int key){//哈希函數
	return key%13;
}

int Linedetect(int HT[],int H0,int key,int &cnt){
    int Hi;
    for(int i=1;i<m;++i){
        cnt++;
        Hi=(H0+i)%m; //按照線性探測法計算下一個哈希位址Hi
        if(HT[Hi]==NULLKEY)
            return Hi;	//若單元Hi為空,則所查元素不存在
        else if(HT[Hi]==key)
            return Hi; //若單元Hi中元素的關鍵字為key
    }
    return -1;
}

int Seconddetect(int HT[],int H0,int key,int &cnt){
    int Hi;
    for(int i=1;i<=m/2;++i){
        int i1=i*i;
        int i2=-i1;
        cnt++;
        Hi=(H0+i1)%m; //按照線性探測法計算下一個哈希位址Hi
        if(HT[Hi]==NULLKEY)//若單元Hi為空,則所查元素不存在
            return Hi;
        else if(HT[Hi]==key)//若單元Hi中元素的關鍵字為key
            return Hi;
        cnt++;
        Hi=(H0+i2)%m; //按照線性探測法計算下一個哈希位址Hi
        if(Hi<0)
            Hi+=m;
        if(HT[Hi]==NULLKEY)//若單元Hi為空,則所查元素不存在
            return Hi;
        else if(HT[Hi]==key)//若單元Hi中元素的關鍵字為key
            return Hi;
    }
    return -1;
}

int SearchHash(int HT[],int key){
    //在哈希表HT中查找關鍵字為key的元素,若查找成功,傳回哈希表的單元标号,否則傳回-1
    int H0=H(key); //根據哈希函數H(key)計算哈希位址
    int Hi,cnt=1;
    if(HT[H0]==NULLKEY)//若單元H0為空,則所查元素不存在
        return -1;
    else if(HT[H0]==key){//若單元H0中元素的關鍵字為key,則查找成功
            cout<<"查找成功,比較次數:"<<cnt<<endl;
            return H0;
        }
        else{
            Hi=Linedetect(HT,H0,key,cnt);
            if(HT[Hi]==key){//若單元Hi中元素的關鍵字為key,則查找成功
                cout<<"查找成功,比較次數:"<<cnt<<endl;
                return Hi;
            }
            else
                return -1;	//若單元Hi為空,則所查元素不存在
        }
}

bool InsertHash(int HT[],int key){
    int H0=H(key); //根據哈希函數H(key)計算哈希位址
    int Hi=-1,cnt=1;
    if(HT[H0]==NULLKEY){
        HC[H0]=1;//統計比較次數
        HT[H0]=key;	//若單元H0為空,放入
        return 1;
    }
    else{
        Hi=Linedetect(HT,H0,key,cnt);//線性探測
        //Hi=Seconddetect(HT,H0,key,cnt);//二次探測
        if((Hi!=-1)&&(HT[Hi]==NULLKEY)){
            HC[Hi]=cnt;
            HT[Hi]=key;//若單元Hi為空,放入
            return 1;
        }
    }
    return 0;
}

void print(int HT[]){
    for(int i=0;i<m;i++)
        cout<<HT[i]<<"\t";
    cout<<endl;
}

int main(){
	int x;
	memset(HT,0,sizeof(HT));
	memset(HC,0,sizeof(HC));
	print(HT);
	cout<<"輸入12個關鍵字,存入哈希表中:"<<endl;
	for(int i=0;i<12;i++){
		cin>>x;
		if(!InsertHash(HT,x)){
		    cout<<"建立哈希表失敗!"<<endl;
		    return 0;
		}
	}
	cout<<"輸出哈希表:"<<endl;
	print(HT);
	print(HC);
	cout<<"輸入要查找的關鍵字"<<endl;
	cin>>x;
	int result=SearchHash(HT,x);
	if(result!=-1)
		cout<<"在第"<<result+1<<"位置找到"<<endl;
	else
		cout<<"未找到"<<endl;
    return 0;
}
//測試資料:14 36 42 38 40 15 19 12 51 65 34 25
           

8.2  字元串模式比對

BF

#include<iostream>
#include<string>
using namespace std;

int BF(string s,string t,int pos){
	int i=pos,j=0,sum=0;
	int slen=s.length();
	int tlen=t.length();
	while(i<slen&&j<tlen){
        sum++;
        if(s[i]==t[j])//如果相等,則繼續比較後面的字元
			i++,j++;
		else{
			i=i-j+1; //i回退到上一輪開始比較的下一個字元
			j=0;  //j回退到第1個字元
		}
    }
	cout<<"一共比較了"<<sum<<"次"<<endl;
	if(j>=tlen) // 比對成功
		return i-tlen+1;
	else
		return 0;
}

int main(){
	string s,t;
	cin>>s>>t;
	cout<<BF(s,t,0)<<endl;
	return 0;
 }
/*
abaabaabeca
abaabe
*/
/*
aabaaabaaaabea
aaaab
*/

           

 KMP

#include<iostream>
#include<string>
using namespace std;
int slen,tlen,next[1000+5];

void get_next(string t){//求模式串T的next函數
	int j=0,k=-1;
	next[0]=-1;
	while(j<tlen){//模式串t的長度
		if(k==-1||t[j]==t[k])
			next[++j]=++k;
		else
			k=next[k];
	}
	for(int i=0;i<=tlen;i++)
		cout<<next[i]<<" ";
}

void get_next2(string t){ //改進的next函數
	int j=0,k=-1;
	next[0]=-1;
	while(j<tlen){//模式串t的長度
		if(k==-1||t[j]==t[k]){
            j++,k++;
            if(t[j]==t[k])
                next[j]=next[k];
            else
                next[j]=k;
		} 
		else
			k=next[k];
	}
	for(int i=0;i<=tlen;i++)
		cout<<next[i]<<" ";
}

int KMP(string s,string t,int pos){
	int i=pos,j=0,sum=0;
	slen=s.length();
	tlen=t.length();
	get_next(t);
	while(i<slen&&j<tlen){
        sum++;
        if(j==-1||s[i]==t[j])//如果相等,則繼續比較後面的字元
			i++,j++;
		else
			j=next[j];//j回退到next[j]
    }
	cout<<"一共比較了"<<sum<<"次"<<endl;
	if(j>=tlen) // 比對成功
		return i-tlen+1;
	else
		return -1;
}

int main(){
	string s,t;
	cin>>s>>t;
	cout<<KMP(s,t,0)<<endl;
	return 0;
}
/*
abaabaabeca
abaabe
*/
/*
aabaaabaaaabea
aaaab
*/
           

8.3  二叉查找樹

bst

#include<iostream>
using namespace std;
#define ENDFLAG -1
typedef int ElemType;

typedef struct BSTNode{
	ElemType data;	//結點資料域
	BSTNode *lchild,*rchild;	//左右孩子指針
}BSTNode,*BSTree;

BSTree SearchBST(BSTree T,ElemType key)//二叉排序樹的遞歸查找
{
    //若查找成功,則傳回指向該資料元素結點的指針,否則傳回空指針
    if((!T)|| key==T->data)
        return T;
    else if (key<T->data)
            return SearchBST(T->lchild,key);//在左子樹中繼續查找
        else
            return SearchBST(T->rchild,key); //在右子樹中繼續查找
}

void InsertBST(BSTree &T,ElemType e)//二叉排序樹的插入
{
    //當二叉排序樹T中不存在關鍵字等于e的資料元素時,則插入該元素
    if(!T)
    {
        BSTree S=new BSTNode; //生成新結點
        S->data=e;             //新結點S的資料域置為e
        S->lchild=S->rchild=NULL;//新結點S作為葉子結點
        T=S;            		//把新結點S連結到已找到的插入位置
    }
    else if(e<T->data)
            InsertBST(T->lchild,e );//插入左子樹
        else if(e>T->data)
            InsertBST(T->rchild,e);//插入右子樹
}

void CreateBST(BSTree &T )//二叉排序樹的建立
{
    //依次讀入一個關鍵字為key的結點,将此結點插入二叉排序樹T中
    T=NULL;
    ElemType e;
    cin>>e;
    while(e!=ENDFLAG)//ENDFLAG為自定義常量,作為輸入結束标志
    {
        InsertBST(T,e);  //插入二叉排序樹T中
        cin>>e;
    }
}

void DeleteBST(BSTree &T,char key)
{
  //從二叉排序樹T中删除關鍵字等于key的結點
    BSTree p=T;BSTree f=NULL;
    BSTree q;
    BSTree s;
    if(!T) return; //樹為空則傳回
    while(p)//查找
    {
        if(p->data==key) break;  //找到關鍵字等于key的結點p,結束循環
        f=p;                //f為p的雙親
        if (p->data>key)
            p=p->lchild; //在p的左子樹中繼續查找
        else
            p=p->rchild; //在p的右子樹中繼續查找
    }
    if(!p) return; //找不到被删結點則傳回
    //三種情況:p左右子樹均不空、無右子樹、無左子樹
    if((p->lchild)&&(p->rchild))//被删結點p左右子樹均不空
    {
        q=p;
        s=p->lchild;
        while(s->rchild)//在p的左子樹中繼續查找其前驅結點,即最右下結點
        {
            q=s;
            s=s->rchild;
        }
        p->data=s->data;  //s的值指派給被删結點p,然後删除s結點
        if(q!=p)
            q->rchild=s->lchild; //重接q的右子樹
        else
            q->lchild=s->lchild; //重接q的左子樹
        delete s;
    }
    else
    {
        if(!p->rchild)//被删結點p無右子樹,隻需重接其左子樹
        {
            q=p;
            p=p->lchild;
        }
        else if(!p->lchild)//被删結點p無左子樹,隻需重接其右子樹
        {
             q=p;
             p=p->rchild;
        }
        /*――――――――――将p所指的子樹挂接到其雙親結點f相應的位置――――――――*/
        if(!f)
            T=p;  //被删結點為根結點
        else if(q==f->lchild)
                f->lchild=p; //挂接到f的左子樹位置
            else
                f->rchild=p;//挂接到f的右子樹位置
        delete q;
	}
}

void InOrderTraverse(BSTree &T)//中序周遊
{
    if(T)
	{
        InOrderTraverse(T->lchild);
        cout<<T->data<<"\t";
        InOrderTraverse(T->rchild);
	}
}

int main()
{
	BSTree T;
	cout<<"請輸入一些整型數,-1結束"<<endl;
	CreateBST(T);
	cout<<"目前有序二叉樹中序周遊結果為"<<endl;
	InOrderTraverse(T);
	cout<<endl;
	ElemType key;//待查找或待删除内容
	cout<<"請輸入待查找關鍵字"<<endl;
	cin>>key;
	BSTree result=SearchBST(T,key);
	if(result)
        cout<<"找到"<<key<<endl;
	else
        cout<<"未找到"<<key<<endl;
	cout<<"請輸入待删除關鍵字"<<endl;
	cin>>key;
	DeleteBST(T,key);
	cout<<"目前有序二叉樹中序周遊結果為"<<endl;
	InOrderTraverse(T);
	return 0;
}

           

8.4  平衡二叉樹

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

typedef struct AVLNode{
   int data;
   int height;
   struct AVLNode *lchild;
   struct AVLNode *rchild;
}*AVLTree;

AVLTree Empty(AVLTree &T)//删除樹
{
    if(T==NULL) return NULL;
    Empty(T->lchild);
    Empty(T->rchild);
    delete T;
    return NULL;
}

inline int Height(AVLTree T)//計算高度
{
    if(T==NULL) return 0;
    return T->height;
}

void updateHeight(AVLTree &T)
{
     T->height=max(Height(T->lchild),Height(T->rchild))+1;
}

AVLTree LL_Rotation(AVLTree &T)//LL旋轉
{
    AVLTree temp=T->lchild;
    T->lchild=temp->rchild;
    temp->rchild=T;
    updateHeight(T);//更新高度
    updateHeight(temp);
    return temp;
}

AVLTree RR_Rotation(AVLTree &T)//RR旋轉
{
    AVLTree temp=T->rchild;
    T->rchild=temp->lchild;
    temp->lchild=T;
    updateHeight(T);//更新高度
    updateHeight(temp);
    return temp;
}

AVLTree LR_Rotation(AVLTree &T)//LR旋轉
{
     T->lchild=RR_Rotation(T->lchild);
     return LL_Rotation(T);
}

AVLTree RL_Rotation(AVLTree &T)//RL旋轉
{
    T->rchild=LL_Rotation(T->rchild);
    return RR_Rotation(T);
}

AVLTree Insert(AVLTree &T,int x)
{
    if(T==NULL) //如果為空,建立新結點
    {
        T=new AVLNode;
        T->lchild=T->rchild=NULL;
        T->data=x;
        T->height=1;
        return T;
     }
    if(T->data==x) return T;//查找成功,什麼也不做,查找失敗時才插入
    if(x<T->data)//插入到左子樹
    {
        T->lchild=Insert(T->lchild,x);//注意插入後飯後結果挂接到T->lchild
        if(Height(T->lchild)-Height(T->rchild)==2)//插入後看是否平衡,如果不平衡顯然是插入的那一邊高度大
        {                                         //沿着高度大的那條路徑判斷
            if(x<T->lchild->data)//判斷是LL還是LR,即插入的是lchild節點的lchild 還是rchild
                T=LL_Rotation(T);
            else
                T=LR_Rotation(T);
        }
    }
    else//插入到右子樹
    {
        T->rchild=Insert(T->rchild,x);
        if(Height(T->rchild)-Height(T->lchild)==2)
        {
            if(x>T->rchild->data)
                T=RR_Rotation(T);
            else
                T=RL_Rotation(T);
        }
    }
    updateHeight(T);
    return T;
}

AVLTree adjust(AVLTree &T)//删除結點後,需要判斷是否還是平衡,如果不平衡,就要調整
{
    if(T==NULL) return NULL;
    if(Height(T->lchild)-Height(T->rchild)==2)//沿着高度大的那條路徑判斷
    {
        if(Height(T->lchild->lchild)>=Height(T->lchild->rchild))
            T=LL_Rotation(T);
        else
            T=LR_Rotation(T);
    }
    if(Height(T->rchild)-Height(T->lchild)==2)//沿着高度大的那條路徑判斷
    {
        if(Height(T->rchild->rchild)>=Height(T->rchild->lchild))
            T=RR_Rotation(T);
        else
            T=RL_Rotation(T);
    }
    updateHeight(T);
    return T;
}

AVLTree Delete(AVLTree &T,int x)
{
    if(T==NULL) return NULL;
    if(T->data==x)//如果找到删除節點
    {
        if(T->rchild==NULL)//如果該節點的右孩子為NULL,那麼直接删除
        {
            AVLTree temp=T;
            T=T->lchild;
            delete temp;
        }
        else//否則,将其右子樹的最左孩子作為這個節點,并且遞歸删除這個節點的值
        {
           AVLTree temp;
           temp=T->rchild;
           while(temp->lchild)
              temp=temp->lchild;
           T->data=temp->data;
           T->rchild=Delete(T->rchild,T->data);
           updateHeight(T);
        }
        return T;
    }

    if(T->data>x)//調節删除節點後可能涉及的節點
        T->lchild=Delete(T->lchild,x);
    if(T->data<x)
        T->rchild=Delete(T->rchild,x);
    updateHeight(T);
	T=adjust(T);
    return T;
}

void Preorder(AVLTree T)//前序周遊友善看樹的結果
{
    if(T==NULL) return ;
    cout<<T->data<<"\t"<<T->height<<endl;
    Preorder(T->lchild);
    Preorder(T->rchild);
}

 void Inorder(AVLTree T)//中序周遊友善看樹的結果
{
    if(T==NULL) return ;
    Inorder(T->lchild);
    cout<<T->data<<"\t"<<T->height<<endl;
    Inorder(T->rchild);
}

 void Posorder(AVLTree T)//後序周遊友善看樹的結果
{
    if(T==NULL) return ;
    Posorder(T->lchild);
    Posorder(T->rchild);
    cout<<T->data<<"\t"<<T->height<<endl;
}

void show(AVLTree T)
{
    Preorder(T);
    cout<<endl;
    Inorder(T);
    cout<<endl;
    Posorder(T);
}

AVLTree CreateAVL(AVLTree &T)
{
    int n,x;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>x;
        T=Insert(T,x);
    }
    return T;
}
int main()
{
    int x;
    AVLTree root=NULL;
    root=Empty(root);
    CreateAVL(root);
    show(root);
    cin>>x;
    root=Delete(root,x);
    show(root);
    return 0;
}
           

9.1  二分搜尋

#include<iostream>
#include<algorithm>
using namespace std;
const int M=100;
int x,n,i;
int s[M];

int BinarySearch(int s[],int n,int x){//二分查找非遞歸算法
   int low=0,high=n-1;  //low指向有序數組的第一個元素,high指向有序數組的最後一個元素
   while(low<=high){
       int middle=(low+high)/2;  //middle為查找範圍的中間值
       if(x==s[middle])  //x等于查找範圍的中間值,算法結束
          return middle;
       else if(x>s[middle]) //x大于查找範圍的中間元素,則從左半部分查找
              low=middle+1;
            else            //x小于查找範圍的中間元素,則從右半部分查找
              high=middle-1;
    }
    return -1;
}

int recursionBS(int s[],int x,int low,int high){ //二分查找遞歸算法
    //low指向數組的第一個元素,high指向數組的最後一個元素
    if(low>high)              //遞歸結束條件
        return -1;
    int middle=(low+high)/2; //計算middle值(查找範圍的中間值)
    if(x==s[middle])        //x等于s[middle],查找成功,算法結束
        return middle;
    else if(x<s[middle]) //x小于s[middle],則從前半部分查找
            return recursionBS(s,x,low,middle-1);
        else            //x大于s[middle],則從後半部分查找
            return recursionBS(s,x,middle+1,high);
}

int main(){
    cout<<"該數列中的元素個數n為:";
    cin>>n;
    cout<<"請依次輸入數列中的元素:";
    for(i=0;i<n;i++)
       cin>>s[i];
    sort(s,s+n); //二分查找的序列必須是有序的,如果無序需要先排序
    cout<<"排序後的數組為:";
    for(i=0;i<n;i++)
       cout<<s[i]<<" ";
    cout<<endl;
    cout<<"請輸入要查找的元素:";
    cin>>x;
    //i=BinarySearch(s,n,x);
    i=recursionBS(s,x,0,n-1);
    if(i==-1)
		cout<<"該數列中沒有要查找的元素"<<endl;
    else
		cout<<"要查找的元素在第"<<i+1<<"位"<<endl;//位序和下标差1
    return 0;
}
           

9.2  深度優先搜尋

01Knapsack

#include<iostream>
using namespace std;
const int M=105;
int i,j,n,W;//n表示n個物品,W表示背包的容量
double w[M],v[M];//w[i] 表示第i個物品的重量,v[i] 表示第i個物品的價值
bool x[M];  //x[i]表示第i個物品是否放入背包
double cw;  //目前重量
double cp;  //目前價值
double bestp;  //目前最優價值
bool bestx[M]; //目前最優解

double Bound(int i){//計算上界(即已裝入物品價值+剩餘物品的總價值)
    //剩餘物品為第i~n種物品
    int rp=0;
    while(i<=n){//依次計算剩餘物品的價值
        rp+=v[i];
        i++;
    }
    return cp+rp;
}

void Backtrack(int t){//用于搜尋空間數,t表示目前擴充結點在第t層
    if(t>n){//已經到達葉子結點
        for(j=1;j<=n;j++)
            bestx[j]=x[j];
        bestp=cp;//儲存目前最優解
        return ;
    }
    if(cw+w[t]<=W){//如果滿足限制條件則搜尋左子樹
        x[t]=1;
        cw+=w[t];
        cp+=v[t];
        Backtrack(t+1);
        cw-=w[t];
        cp-=v[t];
    }
    if(Bound(t+1)>bestp){//如果滿足限界條件則搜尋右子樹
        x[t]=0;
        Backtrack(t+1);
    }
}

void Knapsack(double W, int n){
    cw=0;//初始化目前放入背包的物品重量為0
    cp=0; //初始化目前放入背包的物品價值為0
    bestp=0; //初始化目前最優值為0
    double sumw=0.0; //用來統計所有物品的總重量
    double sumv=0.0; //用來統計所有物品的總價值
    for(i=1;i<=n;i++){
        sumv+=v[i];
        sumw+=w[i];
    }
    if(sumw<=W){
        bestp=sumv;
        cout<<"放入背包的物品最大價值為: "<<bestp<<endl;
        cout<<"所有的物品均放入背包。";
        return;
    }
    Backtrack(1);
    cout<<"放入背包的物品最大價值為: "<<bestp<<endl;
    cout<<"放入背包的物品序号為: ";
    for(i=1;i<=n;i++){ //輸出最優解
        if(bestx[i]==1)
        cout<<i<<" ";
    }
    cout<<endl;
}

int main(){
    cout<<"請輸入物品的個數n和背包的容量W:";
    cin>>n>>W;
    cout<<"請依次輸入每個物品的重量w和價值v,用空格分開:"<<endl;
    for(i=1;i<=n;i++)
        cin>>w[i]>>v[i];
    Knapsack(W,n);
    return 0;
}
/*測試資料
4 10
2 6
5 3
4 5
2 4
*/
           

 01Knapsack_2

#include<iostream>
#include<algorithm>//sort函數需要該頭檔案 
using namespace std;
const int M=105;
int i,j,n,W;//n表示n個物品,W表示背包的容量
double w[M],v[M];//w[i] 表示第i個物品的重量,v[i] 表示第i個物品的價值
bool x[M];  //x[i]表示第i個物品是否放入背包
double cw;   //目前重量
double cp;   //目前價值
double bestp;  //目前最優價值
bool bestx[M]; //目前最優解

double Bound(int i){//計算上界(即将剩餘物品裝滿剩餘的背包容量時所能獲得的最大價值)
    //i表示剩餘物品為第i~n種物品
    double cleft=W-cw;//剩餘容量
    double brp=0.0;
    while(i<=n&&w[i]<cleft){
        cleft-=w[i];
        brp+=v[i];
        i++;
    }
    if(i<=n)//裝滿背包
        brp+=v[i]/w[i]*cleft;
    return cp+brp;
}

void Backtrack(int t){//用于搜尋空間數,t表示目前擴充結點在第t層
    if(t>n){//已經到達葉子結點
        for(j=1;j<=n;j++)
            bestx[j]=x[j];
        bestp=cp;//儲存目前最優解
        return ;
    }
    if(cw+w[t]<=W){//如果滿足限制條件則搜尋左子樹
        x[t]=1;
        cw+=w[t];
        cp+=v[t];
        Backtrack(t+1);
        cw-=w[t];
        cp-=v[t];
    }
    if(Bound(t+1)>bestp){//如果滿足限制條件則搜尋右子樹
        x[t]=0;
        Backtrack(t+1);
    }
}

struct Object{//定義物品結構體,包含物品序号和機關重量價值
    int id; //物品序号
    double d;//機關重量價值
};

bool cmp(Object a1,Object a2){//按照物品機關重量價值由大到小排序
    return a1.d>a2.d;
}

void Knapsack(int W,int n){
    double sumw=0; //用來統計所有物品的總重量
    double sumv=0; //用來統計所有物品的總價值
    Object Q[n];   //物品結構體類型,用于按機關重量價值(價值/重量比)排序
    double a[n+1],b[n+1];//輔助數組,用于把排序後的重量和價值指派給原來的重量價值數組
    for(i=1;i<=n;i++){
        Q[i-1].id=i;
        Q[i-1].d=1.0*v[i]/w[i];
        sumv+=v[i];
        sumw+=w[i];
    }
    if(sumw<=W){
        bestp=sumv;
        cout<<"放入背包的物品最大價值為: "<<bestp<<endl;
        cout<<"所有的物品均放入背包。";
        return;
    }
    sort(Q,Q+n,cmp);
    for(i=1;i<=n;i++){
        a[i]=w[Q[i-1].id];//把排序後的資料傳遞過去
        b[i]=v[Q[i-1].id];
    }
    for(i=1;i<=n;i++){
        w[i]=a[i];//把排序後的資料傳遞過去
        v[i]=b[i];
        //cout<<"排序後的重量和價值為: "<<w[i]<<"  "<<v[i]<<endl;
    }
    Backtrack(1);
    cout<<"放入背包的物品最大價值為: "<<bestp<<endl;
    cout<<"放入背包的物品序号為: ";
    for(i=1;i<=n;i++){
        if(bestx[i]==1)
			cout<<Q[i-1].id<<" ";
    }
    cout<<endl;
}

int main(){
    cout<<"請輸入物品的個數n和背包的容量W:";
    cin>>n>>W;
    cout<<"請依次輸入每個物品的重量w和價值v,用空格分開:"<<endl;
    for(i=1;i<=n;i++)
        cin>>w[i]>>v[i];
    Knapsack(W,n);
    return 0;
}
/*測試資料
4 10
2 6
5 3
4 5
2 4
*/
           

 permutation

#include<iostream>//1..n的全排列
#define MX 50
using namespace std;
int x[MX];  //解分量
int n;

void myarray(int t){
    if(t>n){
        for(int i=1;i<=n;i++) // 輸出排列
            cout<<x[i]<<" ";
        cout<<endl;

        return ;
    }
    for(int i=t;i<=n;i++){  // 枚舉
       swap(x[t],x[i]);  // 交換
       myarray(t+1);  // 繼續深搜
       swap(x[t],x[i]); // 恢複
    }
}

int main(){
    cout<<"輸入排列的元素個數n(求1..n的排列):"<<endl;
    cin>>n;
    for(int i=1;i<=n;i++) //初始化
       x[i]=i;
    myarray(1);
    return 0;
}
           

 queen

//program 5-4
#include<iostream>
#include<cmath>   //求絕對值函數需要引入該頭檔案
#define M 105
using namespace std;

int n;//n表示n個皇後
int x[M];  //x[i]表示第i個皇後放置在第i行第x[i]列
long long countn;    //countn表示n皇後問題可行解的個數

bool Place(int t) //判斷第t個皇後能否放置在第i個位置
{
    bool ok=true;
    for(int j=1;j<t;j++)   //判斷該位置的皇後是否與前面t-1個已經放置的皇後沖突
    {
       if(x[t]==x[j]||t-j==fabs(x[t]-x[j]))//判斷列、對角線是否沖突
       {
           ok=false;
           break;
       }
    }
    return ok;
}

void Backtrack(int t)
{
    if(t>n)  //如果目前位置為n,則表示已經找到了問題的一個解
    {
        countn++;
        for(int i=1; i<=n;i++) //列印選擇的路徑
          cout<<x[i]<<" ";
        cout<<endl;
        cout<<"----------"<<endl;
    }
    else
        for(int i=1;i<=n;i++) //分别判斷n個分支,特别注意i不要定義為全局變量,否則遞歸調用有問題
        {
            x[t]=i;
            if(Place(t))
                Backtrack(t+1); //如果不沖突的話進行下一行的搜尋
        }
}

int main()
{
    cout<<"請輸入皇後的個數 n:";
    cin>>n;
    countn=0;
    Backtrack(1);
    cout <<"答案的個數是:"<<countn<< endl;
    return 0;
}
           

 9.3  廣度優先搜尋

01Knapsack

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int N=10;
bool bestx[N];
//定義結點。每個節點來記錄目前的解。
struct Node{
    int cp,rp; //cp為目前裝入背包的物品總價值,rp為剩餘物品的總價值
    int rw; //剩餘容量
    int id; //物品号
    bool x[N];//解向量
    Node(){}
    Node(int _cp,int _rp,int _rw,int _id){
        cp=_cp;
        rp=_rp;
        rw=_rw;
        id=_id;
        memset(x,0,sizeof(x));//解向量初始化為0
    }
};

struct Goods{//物品
    int weight;//重量 
    int value;//價值 
}goods[N];

int bestp,W,n,sumw,sumv;
/*
  bestp用來記錄最優解
  W為背包最大容量。
  n為物品的個數。
  sumw為所有物品的總重量。
  sumv為所有物品的總價值。
*/

int bfs(){//隊列式分支限界法 
    int t,tcp,trp,trw;//目前處理的物品序号t,裝入背包物品價值tcp,剩餘容量trw
    queue<Node> q; //建立一個普通隊列(先進先出)
    q.push(Node(0,sumv,W,1)); //壓入一個初始節點
    while(!q.empty()){
        Node livenode,lchild,rchild;//定義三個結點型變量
        livenode=q.front();//取出隊頭元素作為目前擴充結點livenode
        q.pop(); //隊頭元素出隊
        t=livenode.id;//目前處理的物品序号
        // 搜到最後一個物品的時候不需要往下搜尋
        // 如果目前的背包沒有剩餘容量(已經裝滿)了,不再擴充
        if(t>n||livenode.rw==0){
            if(livenode.cp>=bestp){//更新最優解和最優值
				for(int i=1;i<=n;i++)
					bestx[i]=livenode.x[i];
              bestp=livenode.cp;
            }
            continue;
        }
        if(livenode.cp+livenode.rp<bestp)//判斷目前結點是否滿足限界條件,如果不滿足不再擴充
			continue;
        tcp=livenode.cp; //目前背包中的價值
        trp=livenode.rp-goods[t].value; //不管目前物品裝入與否,剩餘價值都會減少。
        trw=livenode.rw; //背包剩餘容量
        if(trw>=goods[t].weight){ //擴充左孩子,滿足限制條件,可以放入背包
            lchild.rw=trw-goods[t].weight;
            lchild.cp=tcp+goods[t].value;
            lchild=Node(lchild.cp,trp,lchild.rw,t+1);
            for(int i=1;i<t;i++)//複制以前的解向量
            	lchild.x[i]=livenode.x[i];
            lchild.x[t]=true;
            if(lchild.cp>bestp)//比最優值大才更新
               bestp=lchild.cp;
            q.push(lchild);//左孩子入隊
        }
        if(tcp+trp>=bestp){//擴充右孩子,滿足限界條件,不放入背包
            rchild=Node(tcp,trp,trw,t+1);
            for(int i=1;i<t;i++)//複制以前的解向量
				rchild.x[i]=livenode.x[i];
            rchild.x[t]=false;
            q.push(rchild);//右孩子入隊
        }
    }
    return bestp;//傳回最優值
}

int main(){
    cin>>n>>W;//輸入物品的個數和背包的容量
    bestp=0; //bestv用來記錄最優解
    sumw=0; //sumw為所有物品的總重量。
    sumv=0;  //sumv為所有物品的總價值
    for(int i=1;i<=n;i++){//輸入每個物品的重量和價值,用空格分開
        cin>>goods[i].weight>>goods[i].value;//輸入第i件物品的重量和價值。
        sumw+=goods[i].weight;
        sumv+=goods[i].value;
    }
    if(sumw<=W){
        bestp=sumv;
        cout<<"放入背包的物品最大價值為: "<<bestp<<endl;
        return 0;
    }
    bfs();
    cout<<"放入背包的物品最大價值為: "<<bestp<<endl;
    cout<<"放入背包的物品序号為: ";
    for(int i=1;i<=n;i++){// 輸出最優解
        if(bestx[i])
           cout<<i<<"  ";
    }
    return 0;
}

/*測試資料 
4 10
2 6 5 3 4 5 2 4
*/
           

 01Knapsack_2

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int N=10;
bool bestx[N]; //記錄最優解
int w[N],v[N];//輔助數組,用于存儲排序後的重量和價值

struct Node{//定義結點,記錄目前結點的解資訊
    int cp; //已裝入背包的物品價值
    double up; //價值上界
    int rw; //背包剩餘容量
    int id; //物品号
    bool x[N];
    Node() {}
    Node(int _cp,double _up,int _rw,int _id){
        cp=_cp;
        up=_up;
        rw=_rw;
        id=_id;
        memset(x, 0, sizeof(x));
    }
};

struct Goods{ //物品結構體
    int weight;//重量
    int value;//價值
}goods[N];

struct Object{//輔助物品結構體,用于按機關重量價值(價值/重量比)排序
    int id; //序号
    double d;//機關重量價值
}S[N];


bool cmp(Object a1,Object a2){//排序優先級,按照物品機關重量價值由大到小排序
    return a1.d>a2.d;
}

bool operator <(const Node &a, const Node &b){//隊列優先級。up越大越優先
    return a.up<b.up;
}

int bestp,W,n,sumw,sumv;
/*
  bestv 用來記錄最優解。
  W為背包的最大容量。
  n為物品的個數。
  sumw為所有物品的總重量。
  sumv為所有物品的總價值。
*/

double Bound(Node tnode){//計算節點的上界 
    double maxvalue=tnode.cp;//已裝入背包物品價值
    int t=tnode.id;//排序後序号
    double left=tnode.rw;//剩餘容量
    while(t<=n&&w[t]<=left){
        maxvalue+=v[t];
        left-=w[t++];
    }
    if(t<=n)
        maxvalue+=double(v[t])/w[t]*left;
    return maxvalue;
}

int priorbfs(){//優先隊列式分支限界法
    int t,tcp,trw;//目前處理的物品序号t,目前裝入背包物品價值tcp,目前剩餘容量trw
    double tup;  //目前價值上界tup
    priority_queue<Node> q; //建立一個優先隊列
    q.push(Node(0,sumv,W,1));//初始化,根結點加入優先隊列
    while(!q.empty()){
        Node livenode, lchild, rchild;//定義三個結點型變量
        livenode=q.top();//取出隊頭元素作為目前擴充結點livenode
        q.pop(); //隊頭元素出隊
        t=livenode.id;//目前處理的物品序号
        // 搜到最後一個物品的時候不需要往下搜尋。
        // 如果目前的背包沒有剩餘容量(已經裝滿)了,不再擴充。
        if(t>n||livenode.rw==0){
            if(livenode.cp>=bestp){//更新最優解和最優值
            	for(int i=1;i<=n;i++)
            		bestx[i]=livenode.x[i];
            	bestp=livenode.cp;
            }
            continue;
        }
        if(livenode.up<bestp)//如果不滿足不再擴充
        	continue;
        tcp=livenode.cp; //目前背包中的價值
        trw=livenode.rw; //背包剩餘容量
        if(trw>=w[t]){ //擴充左孩子,滿足限制條件,可以放入背包
            lchild.cp=tcp+v[t];
            lchild.rw=trw-w[t];
            lchild.id=t+1;
            tup=Bound(lchild); //計算左孩子上界
            lchild=Node(lchild.cp,tup,lchild.rw,lchild.id);
            for(int i=1;i<=n;i++)//複制以前的解向量
            	lchild.x[i]=livenode.x[i];
            lchild.x[t]=true;
            if(lchild.cp>bestp)//比最優值大才更新
            	bestp=lchild.cp;
            q.push(lchild);//左孩子入隊
        }
        rchild.cp=tcp;
        rchild.rw=trw;
        rchild.id=t+1;
        tup=Bound(rchild);//計算右孩子上界
        if(tup>=bestp){//擴充右孩子,滿足限界條件,不放入
            rchild=Node(tcp,tup,trw,t+1);
            for(int i=1;i<=n;i++)//複制以前的解向量
            	rchild.x[i]=livenode.x[i];
            rchild.x[t]=false;
            q.push(rchild);//右孩子入隊
          }
    }
    return bestp;//傳回最優值。
}

int main(){
    bestp=0; //bestv用來記錄最優解
    sumw=0; //sumw為所有物品的總重量。
    sumv=0;   //sumv為所有物品的總價值
    cin>>n>>W;
    for(int i=1;i<=n;i++){
        cin>>goods[i].weight>>goods[i].value;//輸入第i件物品的重量和價值。
        sumw+=goods[i].weight;
        sumv+=goods[i].value;
        S[i-1].id=i;
        S[i-1].d=1.0*goods[i].value/goods[i].weight;
    }
    if(sumw<=W){
        bestp=sumv;
        cout<<"放入背包的物品最大價值為: "<<bestp<<endl;
        return 0;
    }
    sort(S,S+n,cmp);//按價值重量比非遞增排序
    for(int i=1;i<=n;i++){//把排序後的資料傳遞給輔助數組
        w[i]=goods[S[i-1].id].weight;
        v[i]=goods[S[i-1].id].value;
    }
    priorbfs();//優先隊列分支限界法
    cout<<"放入背包的物品最大價值為: "<<bestp<<endl;
    cout<<"放入背包的物品序号為: ";
    for(int i=1;i<=n;i++){ //輸出最優解
        if(bestx[i])
           cout<<S[i-1].id<<" ";//輸出原物品序号(排序前的)
    }
    return 0;
}

/*測試資料 
4 10
2 6 5 3 4 5 2 4
*/
           

繼續閱讀