数据结构与算法——学习手册


本手册原创整理 教程参考MOOC 邓俊辉老师的《数据结构》

写在前面的话:

  1. 上联:自学是门手艺,下联:记录是块招牌, 横批:四海为家

  2. 计算机系统,计算机语言分类,程序开发过程,补码原码啥的傻傻搞不清,强推清华 郑莉老师的《C++程序设计基础》~

  3. 面向对象思想是工程项目的核心,继承与派生、多态性、泛型程序设计是核心章节!仍强推清华的《面向对象》~

  4. 要学好机器学习,数据结构是基础!根据斯宾浩斯遗忘曲线,要时常复习!温故而知新!

△基础知识还是多上网课,少自己瞎琢磨,此记做日后的学习警醒!

辅助教材:
《C++程序设计基础》清华大学出版社 郑莉
《深入理解计算机系统》

chapter 0 绪论

算法效率

主要从时间复杂度T(n)和空间复杂度分析 S(n)。
对于处理规模为n的数据的算法,一般算法只分析T(n), 用渐进上界O( f( n ) )表示,但在递归算法中往往要计算时间成本O(?)

  • 常见的算法的T(n):
    如O(1) :
for( i= 1; i < n; 1 << i); 
// or
do { n ++; } while( 2 == ( n *  n ) % 5)

** 循环、递归也可能是O(1)

O(2^n):

_int64 Fib_basic( int n) { return (2 > n ) ? (_int64) n : fib( n - 1) + fib( n - 2); }

O(n): 改进上述的Fib(), 用上楼问题的算法解决

_int64 Fib_updated( int n) {
	_int 64 fun1 = 0; fun2 = 1;
	while (0 < n --) {
	fun2 += fun1; fun1= fun2 - fun1; }
	return fun1;
	}

其他…
图片来自邓俊辉老师的PPT

题目讲解

动态规划:最长公共子序列(LCS)

  1. 问题描述

求两个字符串 A = “immaculate”, B = “computer” 最长公共子序列的长度(A,B)

  1. 解题思路

(1)dynamic programming (recommand)

自底向迭代,避免运算大量重复子问题,最大运算量Θ(n * m)

L C S ( A [ 0 , i ] , B [ 0 , j ] ) = { 0   i f   i  = 0 or j = 0  L C S ( A [ 0 , i − 1 ] , B [ 0 , j − 1 ] ) + 1   i f   A [ i ] = B [ j ]   a n d   L C S ( A [ 0 , i ] , B [ 0 , j − 1 ] ) + 1 ⩾ L C S ( A [ 0 , i − 1 ] , B [ 0 , m ] ) m a x { L C S ( A [ 0 , i − 1 ] , B [ 0 , j ] ) , L C S ( A [ 0 , i ] , B [ 0 , j − 1 ] ) }   i f   n ≠ m   LCS( A[0, i], B[0, j]) = \begin{cases} 0& \text{ $if$ $i$ = 0 or j = 0 } \\ LCS( A[0, i - 1], B[0, j - 1]) + 1& \text{ $if$ $A[i] = B[j] $ $and$ $LCS( A[0, i ], B[0, j - 1]) + 1 \geqslant LCS( A[0, i - 1], B[0, m ])$} \\ max \{LCS( A[0, i - 1], B[0, j ]),LCS( A[0, i ], B[0, j - 1])\} & \text{ $if$ $n \neq m$ } \end{cases} LCS(A[0,i],B[0,j])=0LCS(A[0,i1],B[0,j1])+1max{LCS(A[0,i1],B[0,j]),LCS(A[0,i],B[0,j1])} if i = 0 or j = 0  if A[i]=B[j] and LCS(A[0,i],B[0,j1])+1LCS(A[0,i1],B[0,m]) if n=m 

  • 注:A[0, i] 表示从A的下标 0 到 j 的子序列

/*求A[0, n] 和 B[0, m] 的LCS(最长公共子序列)*/

//LCS函数
string LCS(const string &A, const string &B) {

	//两字符串大小
	unsigned n = A.size();
	unsigned m = B.size();


	//定义二维 n*m LCS长度表
	vector<vector<int> > LCS_length(n + 1, vector<int>(m + 1, 0));
	string LCS; //用string即可满足动态长度要求

	//i == 0 || j == 0, LCS_length = 0
		//定义时已完成

	//从取A[0]开始,与B的子序列对比,记录LCS长度
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {

			//A[i] = B[j]时, LCS_length[i][j] = LCS_length[i - 1][j - 1] + 1
			if (A[i - 1] == B[j - 1]) {//i,j 从1开始,故要-1
				LCS_length[i][j] = LCS_length[i - 1][j - 1] + 1;
				//LCS = LCS + A[i - 1]; //errro
			}
		
			//A[i] != B[j]时, LCS_length[i][j] = max(LCS_length[i - 1][j] , LCS_length[i][j - 1])
			else 
				LCS_length[i][j] = (LCS_length[i][j - 1] > LCS_length[i - 1][j] ?
					LCS_length[i][j - 1] : LCS_length[i - 1][j]);
		
		}
	}

	//输出LCS长度表
	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= m; j++)
			cout << LCS_length[i][j] << ' ';
		cout << endl;
	}

	//返回LCS(A, B)的长度
	return LCS;
}

(2)decrease - and - conquer(not recommand)
自顶向下,会产生大量重复子问题。当n = m 时,LCS(A[0, n], B[0, m])到 LCS(A[0, 0], B[0, 0])会重复计算n 个子问题,最大运算量Θ(2^n)
L C S ( A [ 0 , n ] , B [ 0 , m ] ) = { 0   i f   n  == 0 or m == 0  L C S ( A [ 0 , n − 1 ] , B [ 0 , m − 1 ] )   i f  n ==  m   m a x { L C S ( A [ 0 , n − 1 ] , B [ 0 , m ] ) , L C S ( A [ 0 , n ] , B [ 0 , m − 1 ] ) }   i f  n !=  m   LCS( A[0, n], B[0, m]) = \begin{cases} 0& \text{ $if$ $n$ == 0 or m == 0 } \\ LCS( A[0, n - 1], B[0, m - 1])& \text{ $if$ n == $m$ } \\ max \{LCS( A[0, n - 1], B[0, m ]),LCS( A[0, n ], B[0, m - 1])\} & \text{ $if$ n != $m$ } \end{cases} LCS(A[0,n],B[0,m])=0LCS(A[0,n1],B[0,m1])max{LCS(A[0,n1],B[0,m]),LCS(A[0,n],B[0,m1])} if n == 0 or m == 0  if n == m  if n != m 

//代码待补!

chapter 02 vector

Basic

  1. 类的组合
  2. 继承与派生
  3. 多态性
  4. 泛型程序设计与STL库
    用STL库函数一定记得要加上域名啊,血的教训啊:
using namespace std;  //千万别忘了

写模板类的注意点

1.结合律

左结合: a *= b -> first b then a * b
右结合:a && b -> first a then b

vector 类模板

#pragma once

typedef int Rank;  //秩
#define DEFAULT_CAPACITY 50  //默认初始容量 50

template <typename T> class Vector {
protected:
	Rank _size; int _capacity; T* _elem;  //规模、容量、数据区指针(未初始化)
	void shrink();  //空间太大缩容
public: 
//constructor
	Vector(int capacity = DEFAULT_CAPACITY, int size = 0, T e = 0) {
		//assert size < DEFAULT_CAPACITY
		_elem = new T[_capacity = capacity];  //动态分配内存
		for (_size = 0; _size < size; _elem[_size++] = e);  //所有元素初始化为0/NULL
	}
//read
	Rank size() const { return _size; }  //获取规模

//read & write
	T& operator[] ( Rank r ) { return _elem[r]; };  //重载[], vector[r]返回对应元素
	const T& operator[] (Rank r) { return _elem[r]; } const ;  //重载[], 针对常vector, 功能同上

	Rank insert( Rank r, T const& e ); //插入元素
	int uniquify();  //有序去重
};

具体成员函数实现见代码分享
提取码:ch21


chapter 03 list

Basic

  • const类型
  1. const type var_name—常量,只能在初始化时赋值,其余任何时候不能赋值
  2. *type const pointer/ref_name—顶层指针/引用:指针及其所指量均为常量
  3. *const type pointer/ref_name—底层指针/引用:指针非常量,但所指量为常量
  4. const type fun_name —常量函数:函数返回值为常量
  5. type fun_name const —常函数:函数内部不能出现赋值语句

List 类模板

//list类

#pragma once

#include"listNode.h"  //include ListNode类

template <typename T> class List {  //List类模板

private:  //size of elements, header_pointer, tailer_pointer
	int _size = 0; ListNodeDirect(T) header;  ListNodeDirect(T) trailer; 

protected:
	void init();  //创建空列表
	int clear();  //清空列表
	void copyNodes ( ListNodeDirect(T) pointer, int n );  //复制从pointer所指节点开始的 n 个节点
//排序方法
	void insertionSort( ListNodeDirect(T) pointer, int n );  //插入排序:从pointer所指节点开始的 n 个节点
	void selectionSort( ListNodeDirect(T) pointer, int n );  //选择排序:从pointer所指节点开始的 n 个节点
	void mergeSort( ListNodeDirect(T) pointer, int n );      //归并排序:从pointer所指节点开始的 n 个节点


public:
//constructor
	List() { init(); }  //default
	List( List<T> const& L );  //copy whole List
	List( List<T> const& L, Rank r, int n );  //copy List[r, r + n)
	List( ListNodeDirect(T) pointer, int n );  //copy 从pointer->node 开始的n个
	
//destructor
	~List();  

//read
	Rank size() const { return _size; }  //get rank(常函数)
	bool empty() const { return _size <= 0; }  //isEmpty(常函数)
	Rank nodeRank( T const& e );  //get rank of node
	ListNodeDirect(T) fist() const { return header->succ; }  //get first node
	ListNodeDirect(T) last() const { return tailer->succ; }  //get last node

	//search
	ListNodeDirect(T) find( T const& e, int n, ListNodeDirect(T) p ) const;  //无序区间查找
	ListNodeDirect(T) search( T const& e, int n, ListNodeDirect(T) p) const;  //有序区间查找
	ListNodeDirect(T) search( T const& e ) const { return search( e, _size, tailer )};  //有序全区查找
	ListNodeDirect(T) searchMax( ListNodeDirect(T) p, int n );  //全区查找最大

//read & write
	//insert
	ListNodeDirect(T) insertAsSucc( T const& e );  //插入为后继
	ListNodeDirect(T) insertAsPred( T const& e );   //插入为前驱
	ListNodeDirect(T) insertAsFirst( T const& e );  //插入为首节点
	ListNodeDirect(T) insertAsLast( T const& e );   //插入为末节点
	ListNodeDirect(T) insertAfter( ListNodeDirect(T) position, T const& e );    //插入为后继
	ListNodeDirect(T) insertBefore( ListNodeDirect(T) position, T const& e );   //插入为前驱

	//remove
	T remove( ListNodeDirect(T) position );  //删除位置p处节点

	//reverse
	void reverse();  //前后倒置
};

#include "list_implementation.h"

具体成员函数实现见代码分享
提取码:fhj0

chapter 04 Stack & Queue

Basic

  • 进制表示
    0b表示二进制,eg: int a = 0b10; //int型 a 被初始化为10(2)
    类似0表示八进制,0x表示十六进制

难点分析

调用栈

  • 调用栈原理
    在执行2进制程序时,后台会调用栈(call stack)

    每调用一次函数,都会重复四部曲:压入栈并创建帧—> 记录返回地址—> 记录局部变量、传入参数—>记录前一帧起始地址

每return一次(return给自己,可能白给或被利用),栈都会弹出栈顶,并返回前一帧的地址

  • 迭代代替recursion思想
    因为有调用栈,一个函数可以多次被调用而单独存在于栈中,用recursion容易出现大量重复计算函数,时间空间开销都大

题目讲解

括号匹配问题

/*代码验证: 代码4.4 括号匹配算法*/
//paren.h

#include<iostream>

using namespace std;

//删除表达式exp[lo, hi]不含()的前、后缀
void trim(const char exp[], int& lo, int& hi) {
	while ((lo <= hi) && (exp[lo] != '(') && (exp[lo] != ')')) lo++;
	while ((lo <= hi) && (exp[hi] != '(') && (exp[hi] != ')')) hi--;
}

//从exp[lo, hi]中切分出局部匹配区间
int divide(const char exp[], int lo, int hi) {
	//mi从lo开始遍历, crc表示已扫描区间的 (num("(") - num(")"))的差
	int mi = lo, crc = 1;//初始化mi读入'(' , 故crc初始化为1

	//遍历,直到找到区间 or mi越界
	while ((0 < crc) && (++mi < hi)) {  /*邓老师,请看这里!*/
		if(exp[mi] == ')') crc--; if (exp[mi] == '(') crc++; } //计算左、右括号数的差
	cout << "crc:"<< crc << endl;
	return mi;	//确定切分点
}

bool paren(const char exp[], int lo, int hi) {
	trim(exp, lo, hi); if (lo > hi) return true; //删除exp[lo, hi]不含()的前、后缀
	if (exp[lo] != '(' || exp[hi] != ')') return false; //起始区间不匹配
	int mi = divide(exp, lo, hi); //确定切分点
	if (mi > hi) return false; //mi越界,则crc一定!0
	return paren(exp, lo + 1, mi - 1) && paren(exp, mi + 1, hi);//递归检查左右exp[]
}
//以上为原配方
//*********************************************************************************
//以下为加了个“=”的配方
int divide_add(const char exp[], int lo, int hi) {
	//mi从lo开始遍历, crc表示已扫描区间的 (num("(") - num(")"))的差
	int mi = lo, crc = 1;//初始化mi读入'(' , 故crc初始化为1

	//遍历,直到找到区间 or mi越界
	while ((0 < crc) && (++mi <= hi)) {  /**************邓老师,我改了这里!***************/
		if (exp[mi] == ')') crc--; if (exp[mi] == '(') crc++;
	} //计算左、右括号数的差
	cout << "crc:" << crc << endl;
	return mi;	//确定切分点
}

bool paren_add(const char exp[], int lo, int hi) {
	trim(exp, lo, hi); if (lo > hi) return true; //删除exp[lo, hi]不含()的前、后缀
	if (exp[lo] != '(' || exp[hi] != ')') return false; //起始区间不匹配
	int mi = divide_add(exp, lo, hi); //确定切分点
	if (mi > hi) return false; //mi越界,则crc一定!0
	return paren_add(exp, lo + 1, mi - 1) && paren_add(exp, mi + 1, hi);//递归检查左右exp[]
}

问题详见邓俊辉老师的《数据结构》P 92


chapter05 BinTree

Basic

类成员的访问控制

  1. private:
  • 除了类内成员函数定义中可以直接访问,其他时候都不可访问(包括本类对象、其他类成员函数等)
  • 不管哪种继承方式,在派生类成员函数内都不可直接访问
  1. protect:
  • 除了类内成员函数定义中可以直接访问,其他时候都不可访问(包括本类对象、其他类成员函数等)
  • public或者protect 继承时,可以在派生类成员函数内直接访问
  1. protect:
  • 本类对象可以直接访问。eg: ogj. 某公有成员或公有成员函数
  • 类内成员函数定义中可以直接访问
  • public或者protect 继承时,可以在派生类成员函数内直接访问’

函数模板参数传递

  1. 引用传递
    定义: funciton( type const& ref_name)
    实例:function( int_a ) ; // int const& ref_name = int_a

  2. 地址传递
    定义: funciton( type* pointer_name)
    实例:function( &int_a ) ; // int* pointer_name = &int_a
    function( array_a ); // int* pointer_name = array_a

模板类对象指针

#define ClassName(T) ClassName<T>*

int main(){
	ClassName(int) object = new ClassName<int>(); //调用ClassName的default constructor, 返回int型模板类指针
	  /*对象指针必须初始化才能使用*/
}

难点分析

线索二叉树

参考:
1.彻底理解线索二叉树
2.代码见邓公讲义05-F4–中序遍历:前驱与后继


chapter05 BinTree

Basic

  1. 多文件编译
    · 不能手动改.cpp, .h等后缀
  • 记得加条件编译:
    #ifndef
    #define
    #endif
  • 用scanf()输入string (最好别这么干!)
string person //string类 person
person.resize(3); //person为长度3的字符串
int person_weight; //权值
//read data
//input: AAA 10
scanf("%3s %d", &person[0], &edge_weight);  
//person: AAA, edge_weight: 10
  • 二维vector创建
#include<vector>
vector< vector<int> > persons(5,  vector<int>(0));
//创建二维vector, 实际vector里无任何元素
  • 类定义
  1. 用struct 类名{}; or class 类名{};
  2. 类定义必须先于类对象,不要把类定义和对象定义放在一起
  3. 对象数组:type_name array_name[ const int ];

二叉树模板

//普通二叉树模板:
#pragma once

#include"BTNode.h"  //BinTree node

template <typename T> class BST {
protected:
	int _size;  //树规模
	BinTreeNodeDirect(T) _root;  //根节点
	virtual int updateHeight ( BinTreeNodeDirect(T) x );  //更新节点高度
	void updateHeightAndAbove(BinTreeNodeDirect(T) x );  //更新节点及祖先高度

public:
	int size() const { return _size; }  //total node number 
	bool empty() const { return !_root; }  //is BinTree empty

	BinTreeNodeDirect(T) search(const T& e);  //查找
	bool insert(const T& e);  //插入
	bool remove(const T& e);  //删除
};

#include"BinTree_IsAndHas.h"
#pragma once

二叉搜索数模板

//二叉排序树模板:
#pragma once

#include"BSTNode.h"  //BinTree node

template <typename T> class BST {
protected:
	int _size;  //关键码总数
	BinTreeNodeDirect(T) _root;
	BinTreeNodeDirect(T) _hot; //BTree::search()最后访问的非空(除非树空)的节点位置

	
public:
	int size() const { return _size; }  //total node number 
	bool empty() const { return !_root; }  //is BinTree empty

	BinTreeNodeDirect(T) search( const T& e );  //查找
	bool insert (const T& e );  //插入
	bool remove ( const T& e);  //删除
};

#include"BinTree_IsAndHas.h"

具体成员函数实现见代码分享
提取码:1iwy


chapter 06 graph

Basic knowledge

  • 离散数学中关于图论和树的部分。建议学习MOOC 哈工大的《离散数学》+ 《Discrete Mathematics and Its Applications 7th》

Concepts about Graph

在这里插入图片描述

图应用实例代码

最小生成树

  • 题目描述:从 N 个城市, M条道路中生成连接N个城市的最小生成树,并输出树权

  • input :

6 10   //line1: num_cities num_roads
0 1 4  //line2 ~ M + 1: id_ct1 id_ct2 weightE[id_ct1][id_ct2]
0 4 1
0 5 2
1 2 1
1 5 3
2 3 6
2 5 5
3 4 5
3 5 4 
4 5 3
  • output:

11

Prim
//Prim.h
//Prim算法——只能用于求最小生成树,不能求路径

#ifndef PRIM
#define PRIM

#include<iostream>
#include<algorithm> //使用fill()函数
using namespace std;

#define INF 10000000  //infinity
#define CAPACITY 510  //arrary capacity

//given
int num_cities, num_roads, id_start, id_end;  //line 1 given
int weightE[CAPACITY][CAPACITY];  //line3 give, matrix
//required
int opt_WeightTree = 0;  //min pathWeight
//assistance
bool IsVisited[CAPACITY] = { false };
int SetToV[CAPACITY];  //the optWeight of edge from V to Set
//#define HaveEdge( id_pre, id_ct ) (weightE[id_pre][id_ct] != INF)
#define NewSetToV(id_pre, id_ct) ( weightE[id_pre][id_ct] < SetToV[id_ct])
#define UpdatWeightTree(MIN) (opt_WeightTree += MIN)
#define UpdateSetToV(id_pre, id_ct)  ( SetToV[id_ct] <  weightE[id_pre][id_ct])
int Prim() {

	fill(SetToV, SetToV + CAPACITY, INF);  //initialize
	SetToV[id_start] = 0;  //任取0放入集合Set
	
	for (int id = 0; id < num_cities; id++) {  //traverse n vertexs
		//每次找前驱时initialize
		int id_pre = -1,  MIN = INF;

		for (int j = 0; j < num_cities; j++) {  //try to find pre
			if (!IsVisited[j] && SetToV[j] < MIN) { //unVisited & the closest city to Set
				id_pre = j;  //get id of city
				MIN = SetToV[j];  //update closest value
			}
		}

		//cannot find
		if (id_pre == -1) return;  // traverse next vertex
		//find
		IsVisited[id_pre] = true;  //mark
		UpdatWeightTree(MIN);

		//优化各点到Set距离
		for (int id_ct = 0; id_ct < num_cities; id_ct++) {
			if (!IsVisited[id_ct] && NewSetToV(id_pre, id_ct))  //unVisited & reachable
				UpdateSetToV(id_pre, id_ct);
		}
	}
	return opt_WeightTree;
}


#endif // !PRIM
Kruskal
//待更新,含并查集知识

多解最短路径

  • 题目描述:详见PAT A 1030 Travel Plan
Dijkstra + DFS
//Dijkstra + DFS, 也可以单独用Dijkstra

#ifndef DIJKSTRA_DFS
#define DIJKSTRA_DFS

#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

#define MAX 510  //array capacity
#define INF 100000000  //infinity

//given
int num_cities, num_ways, id_start, id_des;  //define variables for input in line1
int weightDist[MAX][MAX], weightCost[MAX][MAX];  //define 2 weighted graph

//assistance
bool IsVisited[MAX] = { false };  //check vertexs which are visited or not
#define NewOptPath(id_pre, id_ct) ( Dis_optPath[id_pre] + weightDist[id_pre][id_ct] < Dis_optPath[id_ct] )  //is there a smaller total distance
#define UpdateOptDist(id_pre, id_ct) ( Dis_optPath[id_ct] = Dis_optPath[id_pre] + weightDist[id_pre][id_ct] )  //update total distance
#define SameOptPath(id_pre, id_ct) ( Dis_optPath[id_pre] + weightDist[id_pre][id_ct] == Dis_optPath[id_ct] )  //is there a equal total distance but different path
//calculate 
int Dis_optPath[MAX], Cos_optPath = INF;  //optDistance of each city (from id_start) & Cost of optPath (from id_start to id_des)
vector<int> pre[MAX];  //closest predecessor of each city
vector<int> tempPath, optPath;  //temporary path & opt path

void Dijkstra() {

	//initial Distance & Cost of optPath
	fill(Dis_optPath, Dis_optPath + MAX, INF);
	Dis_optPath[id_start] = 0;  //默认 id_start--->id_start 的total distance 为0

	//n个顶点依次作为id_pre
	for (int i = 0; i < num_cities; i++) {
		int id_pre = -1, MIN = INF;  //initialize, easy to find isolated city

		for(int id_ct = 0; id_ct < num_cities; id_ct++)  //find a vertex
			//search for unVisited & min of Dis_optPath
			if (!IsVisited[id_ct] && Dis_optPath[id_ct] < MIN) {
				id_pre = id_ct;
				MIN = Dis_optPath[id_ct];
			}

		if (id_pre == -1) return;  //cannot find 
		//find
		IsVisited[id_pre] = true;  //mark
		for (int id_ct = 0; id_ct < num_cities; id_ct++) {  //遍历n个顶点,check 以id_pre为pred时是否要优化
			if (!IsVisited[id_ct] && weightDist[id_pre][id_ct] < INF) { //unvisited & reachable vertex
				if (NewOptPath(id_pre, id_ct)) {
					UpdateOptDist(id_pre, id_ct);
					pre[id_ct].clear();  //clear predecessor of id_ct
					pre[id_ct].push_back(id_pre);  //update closest predecesspr
				}
				else if( SameOptPath(id_pre, id_ct) )
					pre[id_ct].push_back(id_pre);  //update predecesspr directly
			}
		}
 	}
}

void DFS( int id_ct ) {
	//iteration base
	if (id_ct == id_start) {
		tempPath.push_back(id_ct);  //add id_ct in tempPath
		//calculate tempCost of tempPath
		int tempCost = 0;  //initialize
		for (int i = tempPath.size() - 1; i > 0; i--) {  //add cost from back to forth
			int idV = tempPath[i], idVNext = tempPath[i - 1];  
			tempCost += weightCost[idVNext][idV];  
		} 

		 //is there a smaller optCost
		if (tempCost < Cos_optPath) {
			Cos_optPath = tempCost;  //update Cos_optPath
			optPath = tempPath;  //update optPath
		}
		tempPath.pop_back();  //tempPath的首节点出栈
		return;
	}

	tempPath.push_back(id_ct);  //入栈
	for( unsigned i = 0; i < pre[id_ct].size(); i++)  //每个前驱遍历其所有optPath
		DFS( pre[id_ct][i] );  //顺序选一条optPath深入,直到遇到leaf

	tempPath.pop_back();  //每check完一个tempPath上一个节点, 节点出栈
}

#endif // !DIJKSTRA_DFS

/*
#include"A1030 Travel Plan.h"
int main() {

//input line1
	scanf("%d %d %d %d", &num_cities, &num_ways, &id_start, &id_des);

//input line2~M+1 matrix
	//initialize
	fill(weightDist[0], weightDist[0]+ MAX * MAX, INF);  //weightDist[][] = INF
	fill(weightCost[0], weightCost[0] + MAX * MAX, INF);  //weightCost[][] = INF
	for (int i = 0; i < num_cities; i++)  //self-reachable
		weightDist[i][i] = 0, weightCost[i][i] = 0;
	//input
	int id_ct1 = 0, id_ct2 = 0;
	for (int i = 0; i < num_ways; i++) {
		scanf("%d %d", &id_ct1, &id_ct2);
		scanf("%d", &weightDist[id_ct1][id_ct2]);
		scanf("%d", &weightCost[id_ct1][id_ct2]);

		weightDist[id_ct2][id_ct1] = weightDist[id_ct1][id_ct2];  //directed weightDist graph
		weightCost[id_ct2][id_ct1] = weightCost[id_ct1][id_ct2];  //directed weightCost graph
	}

	//调用算法
	Dijkstra();  //1.记录最短路径每层的最优前驱集合(有多少条Dist_optPath可以到达该层)
	DFS( id_des );  //calculate Cost_optPath based on Dist_optPath

//output
	//line1: cities from id_start--->id_des
	for (int i = optPath.size() - 1; i >= 0; i--)  //从栈顶开始输出
		//optPath.size() == 0 ???d
		printf("%d ", optPath[i]);  //不要加“&”!!!
	//line2: distance & cost
	printf("%d %d\n", Dis_optPath[id_des], Cos_optPath);  //不要加“&”

	return 0;
}
*/

-----------------------我是有底线的---------------------------

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值