链表话题
https://leetcode-cn.com/problems/reverse-linked-list/
#include <iostream>
using namespace std;
struct ListNode{
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL){}
};
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution{
public:
ListNode* reverseList(ListNode *head){
ListNode* pre = NULL;
ListNode* cur = head;
while (cur != NULL){
ListNode* next = cur->next;
// 上面是初始化三个指向
// 进行反转
cur->next = pre;
pre = cur;
cur = next;
}
// null <- 1 <- 3 -> 5 -> 7 -> null
return pre;
}
};
// 1 -> 3 -> 5 -> 7 -> null
// pre cur next
// null -> 1 -> 3 -> 5 -> 7
// 1
// pre指向 1 cur指向 1 next指向3
// null -> 1 -> 3 -> 5 -> 7
// 2
// pre指向 1 cur指向 3 next指向3
// null -> 1 -> 3 -> 5 -> 7
// 3
// pre指向 1 cur指向 3 next指向5
// null -> 1 -> 3 -> 5 -> 7
// 进行反转1
// null <- 1 -> 3 -> 5 -> 7
// 进行反转2
// null <- 1 <- 3 -> 5 -> 7
// pre指向 7 cur指向 next指向
// 1<- 3 <- 5 <- 7
int main (){
// 创建链表
ListNode n1(1);
ListNode n2(3);
ListNode n3(5);
ListNode n4(7);
ListNode n5(9);
n1.next = &n2;
n2.next = &n3;
n3.next = &n4;
n4.next = &n5;
// 遍历
ListNode *head = &n1;
while (head){
cout<< head->val<<"->";
head = head->next;
}
cout<<endl;
cout<<"---------"<<endl;
// 翻转链表
Solution solution;
ListNode* res = solution.reverseList(&n1);
while (res){
cout<< res->val<<"->";
res = res->next;
}
return 0;
}
自己编写测试
#include <iostream>
using namespace std;
/**
* Definition for singly-linked list.
*/
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
/// LinkedList 测试辅助函数
// 根据n个元素的数组arr创建一个链表, 并返回链表的头
ListNode* createLinkedList(int arr[], int n){
if(n == 0)
return NULL;
ListNode* head = new ListNode(arr[0]);
ListNode* curNode = head;
for(int i = 1 ; i < n ; i ++){
curNode->next = new ListNode(arr[i]);
curNode = curNode->next;
}
return head;
}
// 打印以head为头结点的链表信息内容
void printLinkedList(ListNode* head){
ListNode* curNode = head;
while(curNode != NULL){
cout << curNode->val << " -> ";
curNode = curNode->next;
}
cout << "NULL" << endl;
return;
}
// 释放以head为头结点的链表空间
void deleteLinkedList(ListNode* head){
ListNode* curNode = head;
while(curNode != NULL){
ListNode* delNode = curNode;
curNode = curNode->next;
delete delNode;
}
return;
}
// 206. Reverse Linked List
// https://leetcode.com/problems/reverse-linked-list/description/
// 时间复杂度: O(n)
// 空间复杂度: O(1)
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = NULL;
ListNode* cur = head;
while(cur != NULL){
ListNode* next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
int main(){
int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr)/sizeof(int);
ListNode* head = createLinkedList(arr, n);
printLinkedList(head);
ListNode* head2 = Solution().reverseList(head);
printLinkedList(head2);
deleteLinkedList(head2);
return 0;
}
堆
堆的典型应用, 优先队列
典型应用: 操作系统程序调用
医院排队, 有的病人病情比较严重, 先去看病
游戏里面英雄攻击范围的敌人, 动态优先选择
不使用0号索引,存储堆
#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
using namespace std;
template<typename Item>
class MaxHeap{
private:
Item *data;
int count;
public:
// 构造函数, 构造一个空堆, 可容纳capacity个元素
MaxHeap(int capacity){
data = new Item[capacity+1];
count = 0;
}
~MaxHeap(){
delete[] data;
}
// 返回堆中的元素个数
int size(){
return count;
}
// 返回一个布尔值, 表示堆中是否为空
bool isEmpty(){
return count == 0;
}
};
// 测试 MaxHeap
int main() {
MaxHeap<int> maxheap = MaxHeap<int>(100);//容量
cout<<maxheap.size()<<endl; // 元素个数
return 0;
}
堆中插入元素 shift up
void shiftUp(int k){
while( k > 1 && data[k/2] < data[k] ){
// data[k/2]父节点
swap( data[k/2], data[k] );
k /= 2;
}
}
// 像最大堆中插入一个新的元素 item
void insert(Item item){
assert( count + 1 <= capacity );
data[count+1] = item;
count ++;
shiftUp(count);
}
完整代码
#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
using namespace std;
template<typename Item>
class MaxHeap{
private:
Item *data;
int count;
int capacity;
void shiftUp(int k){
while( k > 1 && data[k/2] < data[k] ){
// data[k/2]父节点
swap( data[k/2], data[k] );
k /= 2;
}
}
public:
// 构造函数, 构造一个空堆, 可容纳capacity个元素
MaxHeap(int capacity){
data = new Item[capacity+1];
count = 0;
this->capacity = capacity;
}
~MaxHeap(){
delete[] data;
}
// 返回堆中的元素个数
int size(){
return count;
}
// 返回一个布尔值, 表示堆中是否为空
bool isEmpty(){
return count == 0;
}
// 像最大堆中插入一个新的元素 item
void insert(Item item){
assert( count + 1 <= capacity );
data[count+1] = item;
count ++;
shiftUp(count);
}
public:
// 以树状打印整个堆结构
void testPrint(){
// 我们的testPrint只能打印100个元素以内的堆的树状信息
if( size() >= 100 ){
cout<<"This print function can only work for less than 100 int";
return;
}
// 我们的testPrint只能处理整数信息
if( typeid(Item) != typeid(int) ){
cout <<"This print function can only work for int item";
return;
}
cout<<"The max heap size is: "<<size()<<endl;
cout<<"Data in the max heap: ";
for( int i = 1 ; i <= size() ; i ++ ){
// 我们的testPrint要求堆中的所有整数在[0, 100)的范围内
assert( data[i] >= 0 && data[i] < 100 );
cout<<data[i]<<" ";
}
cout<<endl;
cout<<endl;
int n = size();
int max_level = 0;
int number_per_level = 1;
while( n > 0 ) {
max_level += 1;
n -= number_per_level;
number_per_level *= 2;
}
int max_level_number = int(pow(2, max_level-1));
int cur_tree_max_level_number = max_level_number;
int index = 1;
for( int level = 0 ; level < max_level ; level ++ ){
string line1 = string(max_level_number*3-1, ' ');
int cur_level_number = min(count-int(pow(2,level))+1,int(pow(2,level)));
bool isLeft = true;
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index ++ , index_cur_level ++ ){
putNumberInLine( data[index] , line1 , index_cur_level , cur_tree_max_level_number*3-1 , isLeft );
isLeft = !isLeft;
}
cout<<line1<<endl;
if( level == max_level - 1 )
break;
string line2 = string(max_level_number*3-1, ' ');
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index_cur_level ++ )
putBranchInLine( line2 , index_cur_level , cur_tree_max_level_number*3-1 );
cout<<line2<<endl;
cur_tree_max_level_number /= 2;
}
}
private:
void putNumberInLine( int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft){
int sub_tree_width = (cur_tree_width - 1) / 2;
int offset = index_cur_level * (cur_tree_width+1) + sub_tree_width;
assert(offset + 1 < line.size());
if( num >= 10 ) {
line[offset + 0] = '0' + num / 10;
line[offset + 1] = '0' + num % 10;
}
else{
if( isLeft)
line[offset + 0] = '0' + num;
else
line[offset + 1] = '0' + num;
}
}
void putBranchInLine( string &line, int index_cur_level, int cur_tree_width){
int sub_tree_width = (cur_tree_width - 1) / 2;
int sub_sub_tree_width = (sub_tree_width - 1) / 2;
int offset_left = index_cur_level * (cur_tree_width+1) + sub_sub_tree_width;
assert( offset_left + 1 < line.size() );
int offset_right = index_cur_level * (cur_tree_width+1) + sub_tree_width + 1 + sub_sub_tree_width;
assert( offset_right < line.size() );
line[offset_left + 1] = '/';
line[offset_right + 0] = '\\';
}
};
// 测试 MaxHeap
int main() {
MaxHeap<int> maxheap = MaxHeap<int>(100);
srand(time(NULL));
for( int i = 0 ; i < 10; i ++ )
maxheap.insert( rand()%100 );
maxheap.testPrint();
return 0;
}
取出元素 shift down
取出优先级最高的元素 62
将
#include <iostream>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <ctime>
#include <string>
using namespace std;
// 泛型 模板
template<typename Item>
class MaxHeap{
private:
Item *data;
int count; // 存储的的元素值
int capacity;
void shiftUp(int k){
while (k>1 && data[k/2] < data[k]){
swap(data[k/2], data[k]);
k /= 2;
}
}
void shiftDown(int k){
while (2*k <= count){
int j = 2*k; // 左孩子
if (j+1 <= count && data[j+1] > data[j]) // 变成有孩子
j++;
if (data[k] >= data[j]) break;//找到了自己的位置
swap(data[k], data[j]);
k = j;//掉下来之后就是孩子
}
}
public:
MaxHeap(int capacity){
// 在堆空间创造数组 new int[10];
data = new Item[capacity+1];
count = 0;
this->capacity = capacity;
}
~MaxHeap(){
delete[] data;
}
int size(){
return count;
}
bool isEmpty(){
return count==0;
}
void insert(Item item){
assert(count + 1 <= capacity);
data[count + 1] = item;
count++;
shiftUp(count);
}
void show(){
for (int i = 1; i < 12; i++) {
cout<< data[i] <<" ";
}
}
Item getMax(){
assert(count > 0);
return data[1];
}
Item extractMax(){
assert(count > 0);
Item ret = data[1];
swap(data[1], data[count]);
count--;
shiftDown(1);
return ret;
}
private:
void putNumberInLine( int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft){
int sub_tree_width = (cur_tree_width - 1) / 2;
int offset = index_cur_level * (cur_tree_width+1) + sub_tree_width;
assert(offset + 1 < line.size());
if( num >= 10 ) {
line[offset + 0] = '0' + num / 10;
line[offset + 1] = '0' + num % 10;
}
else{
if( isLeft)
line[offset + 0] = '0' + num;
else
line[offset + 1] = '0' + num;
}
}
void putBranchInLine( string &line, int index_cur_level, int cur_tree_width){
int sub_tree_width = (cur_tree_width - 1) / 2;
int sub_sub_tree_width = (sub_tree_width - 1) / 2;
int offset_left = index_cur_level * (cur_tree_width+1) + sub_sub_tree_width;
assert( offset_left + 1 < line.size() );
int offset_right = index_cur_level * (cur_tree_width+1) + sub_tree_width + 1 + sub_sub_tree_width;
assert( offset_right < line.size() );
line[offset_left + 1] = '/';
line[offset_right + 0] = '\\';
}
public:
void testPrint(){
// 我们的testPrint只能打印100个元素以内的堆的树状信息
if( size() >= 100 ){
cout<<"This print function can only work for less than 100 int";
return;
}
// 我们的testPrint只能处理整数信息
if( typeid(Item) != typeid(int) ){
cout <<"This print function can only work for int item";
return;
}
cout<<"The max heap size is: "<<size()<<endl;
cout<<"Data in the max heap: ";
for( int i = 1 ; i <= size() ; i ++ ){
// 我们的testPrint要求堆中的所有整数在[0, 100)的范围内
assert( data[i] >= 0 && data[i] < 100 );
cout<<data[i]<<" ";
}
cout<<endl;
cout<<endl;
int n = size();
int max_level = 0;
int number_per_level = 1;
while( n > 0 ) {
max_level += 1;
n -= number_per_level;
number_per_level *= 2;
}
int max_level_number = int(pow(2, max_level-1));
int cur_tree_max_level_number = max_level_number;
int index = 1;
for( int level = 0 ; level < max_level ; level ++ ){
string line1 = string(max_level_number*3-1, ' ');
int cur_level_number = min(count-int(pow(2,level))+1,int(pow(2,level)));
bool isLeft = true;
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index ++ , index_cur_level ++ ){
putNumberInLine( data[index] , line1 , index_cur_level , cur_tree_max_level_number*3-1 , isLeft );
isLeft = !isLeft;
}
cout<<line1<<endl;
if( level == max_level - 1 )
break;
string line2 = string(max_level_number*3-1, ' ');
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index_cur_level ++ )
putBranchInLine( line2 , index_cur_level , cur_tree_max_level_number*3-1 );
cout<<line2<<endl;
cur_tree_max_level_number /= 2;
}
}};
int main (){
// vector<int>
MaxHeap<int> maxHeap = MaxHeap<int>(100);
srand(time(NULL));
maxHeap.insert(62);
maxHeap.insert(41);
maxHeap.insert(30);
maxHeap.insert(28);
maxHeap.insert(16);
maxHeap.insert(22);
maxHeap.insert(13);
maxHeap.insert(17);
maxHeap.insert(19);
maxHeap.insert(15);
maxHeap.insert(52);
// for (int i = 0; i < 50; i++) {
// maxHeap.insert(rand()%100);
// }
maxHeap.testPrint();
cout<< "-----" <<endl;
// cout<< maxHeap.getMax() <<endl;
maxHeap.extractMax();
maxHeap.testPrint();
return 0;
};
实现自己的优先队列
#include <iostream>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <ctime>
#include <string>
using namespace std;
// 泛型 模板
template<typename Item>
class PriorityQueue{
private:
Item *data;
int count; // 存储的的元素值
int capacity;
void shiftUp(int k){
while (k>1 && data[k/2] < data[k]){
swap(data[k/2], data[k]);
k /= 2;
}
}
void shiftDown(int k){
while (2*k <= count){
int j = 2*k; // 左孩子
if (j+1 <= count && data[j+1] > data[j]) // 变成有孩子
j++;
if (data[k] >= data[j]) break;//找到了自己的位置
swap(data[k], data[j]);
k = j;//掉下来之后就是孩子
}
}
public:
PriorityQueue(int capacity){
// 在堆空间创造数组 new int[10];
data = new Item[capacity+1];
count = 0;
this->capacity = capacity;
}
~PriorityQueue(){
delete[] data;
}
int size(){
return count;
}
bool isEmpty(){
return count==0;
}
void insert(Item item){
assert(count + 1 <= capacity);
data[count + 1] = item;
count++;
shiftUp(count);
}
void show(){
for (int i = 1; i < 12; i++) {
cout<< data[i] <<" ";
}
}
Item getMax(){
assert(count > 0);
return data[1];
}
Item extractMax(){
assert(count > 0);
Item ret = data[1];
swap(data[1], data[count]);
count--;
shiftDown(1);
return ret;
}
private:
void putNumberInLine( int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft){
int sub_tree_width = (cur_tree_width - 1) / 2;
int offset = index_cur_level * (cur_tree_width+1) + sub_tree_width;
assert(offset + 1 < line.size());
if( num >= 10 ) {
line[offset + 0] = '0' + num / 10;
line[offset + 1] = '0' + num % 10;
}
else{
if( isLeft)
line[offset + 0] = '0' + num;
else
line[offset + 1] = '0' + num;
}
}
void putBranchInLine( string &line, int index_cur_level, int cur_tree_width){
int sub_tree_width = (cur_tree_width - 1) / 2;
int sub_sub_tree_width = (sub_tree_width - 1) / 2;
int offset_left = index_cur_level * (cur_tree_width+1) + sub_sub_tree_width;
assert( offset_left + 1 < line.size() );
int offset_right = index_cur_level * (cur_tree_width+1) + sub_tree_width + 1 + sub_sub_tree_width;
assert( offset_right < line.size() );
line[offset_left + 1] = '/';
line[offset_right + 0] = '\\';
}
public:
void testPrint(){
// 我们的testPrint只能打印100个元素以内的堆的树状信息
if( size() >= 100 ){
cout<<"This print function can only work for less than 100 int";
return;
}
// 我们的testPrint只能处理整数信息
if( typeid(Item) != typeid(int) ){
cout <<"This print function can only work for int item";
return;
}
cout<<"The max heap size is: "<<size()<<endl;
cout<<"Data in the max heap: ";
for( int i = 1 ; i <= size() ; i ++ ){
// 我们的testPrint要求堆中的所有整数在[0, 100)的范围内
assert( data[i] >= 0 && data[i] < 100 );
cout<<data[i]<<" ";
}
cout<<endl;
cout<<endl;
int n = size();
int max_level = 0;
int number_per_level = 1;
while( n > 0 ) {
max_level += 1;
n -= number_per_level;
number_per_level *= 2;
}
int max_level_number = int(pow(2, max_level-1));
int cur_tree_max_level_number = max_level_number;
int index = 1;
for( int level = 0 ; level < max_level ; level ++ ){
string line1 = string(max_level_number*3-1, ' ');
int cur_level_number = min(count-int(pow(2,level))+1,int(pow(2,level)));
bool isLeft = true;
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index ++ , index_cur_level ++ ){
putNumberInLine( data[index] , line1 , index_cur_level , cur_tree_max_level_number*3-1 , isLeft );
isLeft = !isLeft;
}
cout<<line1<<endl;
if( level == max_level - 1 )
break;
string line2 = string(max_level_number*3-1, ' ');
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index_cur_level ++ )
putBranchInLine( line2 , index_cur_level , cur_tree_max_level_number*3-1 );
cout<<line2<<endl;
cur_tree_max_level_number /= 2;
}
}};
int main (){
// vector<int>
PriorityQueue<int> pq = PriorityQueue<int>(20);
for (int i = 0; i < 10; i++) {
int num = rand()%100;
pq.insert(num);
cout<< "insert "<< num << " in priority_queue" <<endl;
}
pq.testPrint();
while (!pq.isEmpty()){
cout<< pq.getMax() <<"\t";
pq.extractMax();
}
cout<< "-----" <<endl;
return 0;
};
优先队列的使用
#include <iostream>
#include <queue>
#include <ctime>
#include <functional>
using namespace std;
bool myCmp(int a , int b){
if(a%10 != b%10)
return a%10 > b%10;
return a > b;
}
int main() {
srand(time(NULL));
// 默认的priority queue, 底层是最大堆
priority_queue<int> pq;
for(int i = 0 ; i < 10 ; i ++){
int num = rand() % 100;
pq.push(num);
cout << "insert " << num << " in priority queue." << endl;
}
while(!pq.empty()){
cout << pq.top() << " ";
pq.pop();
}
cout << endl << endl;
// 使用greater的priority queue, 底层是最小堆
priority_queue<int, vector<int>, greater<int>> pq2;
for(int i = 0; i < 10; i ++){
int num = rand() % 100;
pq2.push(num);
cout << "insert " << num << " in priority queue." << endl;
}
while(!pq2.empty()){
cout << pq2.top() << " ";
pq2.pop();
}
cout << endl << endl;
// 使用自定义Comparator的priority queue
priority_queue<int, vector<int>, function<bool(int,int)>> pq3(myCmp);
for(int i = 0; i < 10; i ++){
int num = rand() % 100;
pq3.push(num);
cout << "insert " << num << " in priority queue." << endl;
}
while(!pq3.empty()){
cout << pq3.top() << " ";
pq3.pop();
}
return 0;
}
索引堆
堆顶元素是索引10指向的62, 比如将系统进程好为7的优先级往前提一提, 就是交换索引就行, 比较的时候比较值,交换的时候交换索引
#include <iostream>
#include <cassert>
#include "SortTestHelper.h"
using namespace std;
// 最大索引堆
template<typename Item>
class IndexMaxHeap{
private:
Item *data; // 最大索引堆中的数据
int *indexes; // 最大索引堆中的索引
int count;
int capacity;
// 索引堆中, 数据之间的比较根据data的大小进行比较, 但实际操作的是索引
void shiftUp( int k ){
while( k > 1 && data[indexes[k/2]] < data[indexes[k]] ){
swap( indexes[k/2] , indexes[k] );
k /= 2;
}
}
// 索引堆中, 数据之间的比较根据data的大小进行比较, 但实际操作的是索引
void shiftDown( int k ){
while( 2*k <= count ){
int j = 2*k;
if( j + 1 <= count && data[indexes[j+1]] > data[indexes[j]] )
j += 1;
if( data[indexes[k]] >= data[indexes[j]] )
break;
swap( indexes[k] , indexes[j] );
k = j;
}
}
public:
// 构造函数, 构造一个空的索引堆, 可容纳capacity个元素
IndexMaxHeap(int capacity){
data = new Item[capacity+1];
indexes = new int[capacity+1];
count = 0;
this->capacity = capacity;
}
~IndexMaxHeap(){
delete[] data;
delete[] indexes;
}
// 返回索引堆中的元素个数
int size(){
return count;
}
// 返回一个布尔值, 表示索引堆中是否为空
bool isEmpty(){
return count == 0;
}
// 向最大索引堆中插入一个新的元素, 新元素的索引为i, 元素为item
// 传入的i对用户而言,是从0索引的
void insert(int i, Item item){
assert( count + 1 <= capacity );
assert( i + 1 >= 1 && i + 1 <= capacity );
i += 1;
data[i] = item;
indexes[count+1] = i;
count++;
shiftUp(count);
}
// 从最大索引堆中取出堆顶元素, 即索引堆中所存储的最大数据
Item extractMax(){
assert( count > 0 );
Item ret = data[indexes[1]];
swap( indexes[1] , indexes[count] );
count--;
shiftDown(1);
return ret;
}
// 从最大索引堆中取出堆顶元素的索引
int extractMaxIndex(){
assert( count > 0 );
int ret = indexes[1] - 1;
swap( indexes[1] , indexes[count] );
count--;
shiftDown(1);
return ret;
}
// 获取最大索引堆中的堆顶元素
Item getMax(){
assert( count > 0 );
return data[indexes[1]];
}
// 获取最大索引堆中的堆顶元素的索引
int getMaxIndex(){
assert( count > 0 );
return indexes[1]-1;
}
// 获取最大索引堆中索引为i的元素
Item getItem( int i ){
assert( i + 1 >= 1 && i + 1 <= capacity );
return data[i+1];
}
// 将最大索引堆中索引为i的元素修改为newItem
void change( int i , Item newItem ){
i += 1;
data[i] = newItem;
// 找到indexes[j] = i, j表示data[i]在堆中的位置
// 之后shiftUp(j), 再shiftDown(j)
for( int j = 1 ; j <= count ; j ++ )
if( indexes[j] == i ){
shiftUp(j);
shiftDown(j);
return;
}
}
// 测试索引堆中的索引数组index
// 注意:这个测试在向堆中插入元素以后, 不进行extract操作有效
bool testIndexes(){
int *copyIndexes = new int[count+1];
for( int i = 0 ; i <= count ; i ++ )
copyIndexes[i] = indexes[i];
copyIndexes[0] = 0;
std::sort(copyIndexes, copyIndexes + count + 1);
// 在对索引堆中的索引进行排序后, 应该正好是1...count这count个索引
bool res = true;
for( int i = 1 ; i <= count ; i ++ )
if( copyIndexes[i-1] + 1 != copyIndexes[i] ){
res = false;
break;
}
delete[] copyIndexes;
if( !res ){
cout<<"Error!"<<endl;
return false;
}
return true;
}
};
// 使用最大索引堆进行堆排序, 来验证我们的最大索引堆的正确性
// 最大索引堆的主要作用不是用于排序, 我们在这里使用排序只是为了验证我们的最大索引堆实现的正确性
// 在后续的图论中, 无论是最小生成树算法, 还是最短路径算法, 我们都需要使用索引堆进行优化:)
template<typename T>
void heapSortUsingIndexMaxHeap(T arr[], int n){
IndexMaxHeap<T> indexMaxHeap = IndexMaxHeap<T>(n);
for( int i = 0 ; i < n ; i ++ )
indexMaxHeap.insert( i , arr[i] );
assert( indexMaxHeap.testIndexes() );
for( int i = n-1 ; i >= 0 ; i -- )
arr[i] = indexMaxHeap.extractMax();
}
int main() {
int n = 1000000;
int* arr = SortTestHelper::generateRandomArray(n, 0, n);
SortTestHelper::testSort("Heap Sort Using Index-Max-Heap", heapSortUsingIndexMaxHeap, arr, n);
delete[] arr;
return 0;
}
SortTestHelper.h
#ifndef INC_08_INDEX_HEAP_SORTTESTHELPER_H
#define INC_08_INDEX_HEAP_SORTTESTHELPER_H
#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cassert>
#include <string>
using namespace std;
namespace SortTestHelper {
// 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]
int *generateRandomArray(int n, int range_l, int range_r) {
int *arr = new int[n];
srand(time(NULL));
for (int i = 0; i < n; i++)
arr[i] = rand() % (range_r - range_l + 1) + range_l;
return arr;
}
// 生成一个近乎有序的数组
// 首先生成一个含有[0...n-1]的完全有序数组, 之后随机交换swapTimes对数据
// swapTimes定义了数组的无序程度
int *generateNearlyOrderedArray(int n, int swapTimes){
int *arr = new int[n];
for(int i = 0 ; i < n ; i ++ )
arr[i] = i;
srand(time(NULL));
for( int i = 0 ; i < swapTimes ; i ++ ){
int posx = rand()%n;
int posy = rand()%n;
swap( arr[posx] , arr[posy] );
}
return arr;
}
// 拷贝整型数组a中的所有元素到一个新的数组, 并返回新的数组
int *copyIntArray(int a[], int n){
int *arr = new int[n];
//* 在VS中, copy函数被认为是不安全的, 请大家手动写一遍for循环:)
copy(a, a+n, arr);
return arr;
}
// 打印arr数组的所有内容
template<typename T>
void printArray(T arr[], int n) {
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
cout << endl;
return;
}
// 判断arr数组是否有序
template<typename T>
bool isSorted(T arr[], int n) {
for (int i = 0; i < n - 1; i++)
if (arr[i] > arr[i + 1])
return false;
return true;
}
// 测试sort排序算法排序arr数组所得到结果的正确性和算法运行时间
// 将算法的运行时间打印在控制台上
template<typename T>
void testSort(const string &sortName, void (*sort)(T[], int), T arr[], int n) {
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s"<<endl;
assert(isSorted(arr, n));
return;
}
// 测试sort排序算法排序arr数组所得到结果的正确性和算法运行时间
// 将算法的运行时间以double类型返回, 单位为秒(s)
template<typename T>
double testSort(void (*sort)(T[], int), T arr[], int n) {
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(arr, n));
return double(endTime - startTime) / CLOCKS_PER_SEC;
}
};
#endif //INC_08_INDEX_HEAP_SORTTESTHELPER_H
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <cassert>
using namespace std;
// 347. Top K Frequent Elements
// https://leetcode-cn.com/problems/top-k-frequent-elements/submissions/
// 时间复杂度: O(nlogk)
// 空间复杂度: O(n + k)
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
assert(k > 0);
// 统计每个元素出现的频率
unordered_map<int,int> freq;
for(int i = 0 ; i < nums.size() ; i ++ )
freq[nums[i]] ++;
assert(k <= freq.size());
// 扫描freq,维护当前出现频率最高的k个元素
// 在优先队列中,按照频率排序,所以数据对是 (频率,元素) 的形式
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> pq;
for(unordered_map<int,int>::iterator iter = freq.begin();
iter != freq.end(); iter ++ ){
if(pq.size() == k){
if(iter->second > pq.top().first){
pq.pop();
pq.push( make_pair(iter->second, iter->first));
}
}
else
pq.push(make_pair(iter->second , iter->first));
}
vector<int> res;
while(!pq.empty()){
res.push_back(pq.top().second);
pq.pop();
}
return res;
}
};
int main() {
int nums[] = {1, 1, 1, 2, 2, 3};
vector<int> vec(nums, nums + sizeof(nums)/sizeof(int));
int k = 2;
vector<int> res = Solution().topKFrequent(vec, 2);
for( int i = 0 ; i < res.size() ; i ++ )
cout<<res[i]<<" ";
cout<<endl;
return 0;
}
最小生成树
邻接矩阵表示带权图
存成指针可以表示空
Edge.h
#ifndef INC_01_WEIGHTED_GRAPH_EDGE_H
#define INC_01_WEIGHTED_GRAPH_EDGE_H
#include <iostream>
#include <cassert>
using namespace std;
// 边
template<typename Weight>
class Edge{
private:
int a,b; // 边的两个端点
Weight weight; // 边的权值
public:
// 构造函数
Edge(int a, int b, Weight weight){
this->a = a;
this->b = b;
this->weight = weight;
}
// 空的构造函数, 所有的成员变量都取默认值
Edge(){}
~Edge(){}
int v(){ return a;} // 返回第一个顶点
int w(){ return b;} // 返回第二个顶点
Weight wt(){ return weight;} // 返回权值
// 给定一个顶点, 返回另一个顶点
int other(int x){
assert( x == a || x == b );
return x == a ? b : a;
}
// 输出边的信息
friend ostream& operator<<(ostream &os, const Edge &e){
os<<e.a<<"-"<<e.b<<": "<<e.weight;
return os;
}
// 边的大小比较, 是对边的权值的大小比较
bool operator<(Edge<Weight>& e){
return weight < e.wt();
}
bool operator<=(Edge<Weight>& e){
return weight <= e.wt();
}
bool operator>(Edge<Weight>& e){
return weight > e.wt();
}
bool operator>=(Edge<Weight>& e){
return weight >= e.wt();
}
bool operator==(Edge<Weight>& e){
return weight == e.wt();
}
};
#endif //INC_01_WEIGHTED_GRAPH_EDGE_H
SparseGraph.h
#ifndef INC_01_WEIGHTED_GRAPH_SPARSEGRAPH_H
#define INC_01_WEIGHTED_GRAPH_SPARSEGRAPH_H
#include <iostream>
#include <vector>
#include <cassert>
#include "Edge.h"
using namespace std;
// 稀疏图 - 邻接表
template<typename Weight>
class SparseGraph{
private:
int n, m; // 节点数和边数
bool directed; // 是否为有向图
vector<vector<Edge<Weight> *> > g; // 图的具体数据
public:
// 构造函数
SparseGraph( int n , bool directed){
assert(n >= 0);
this->n = n;
this->m = 0; // 初始化没有任何边
this->directed = directed;
// g初始化为n个空的vector, 表示每一个g[i]都为空, 即没有任和边
g = vector<vector<Edge<Weight> *> >(n, vector<Edge<Weight> *>());
}
// 析构函数
~SparseGraph(){
for( int i = 0 ; i < n ; i ++ )
for( int j = 0 ; j < g[i].size() ; j ++ )
delete g[i][j];
}
int V(){ return n;} // 返回节点个数
int E(){ return m;} // 返回边的个数
// 向图中添加一个边, 权值为weight
void addEdge( int v, int w , Weight weight){
assert( v >= 0 && v < n );
assert( w >= 0 && w < n );
// 注意, 由于在邻接表的情况, 查找是否有重边需要遍历整个链表
// 我们的程序允许重边的出现
g[v].push_back(new Edge<Weight>(v, w, weight));
if( v != w && !directed )
g[w].push_back(new Edge<Weight>(w, v, weight));
m ++;
}
// 验证图中是否有从v到w的边
bool hasEdge( int v , int w ){
assert( v >= 0 && v < n );
assert( w >= 0 && w < n );
for( int i = 0 ; i < g[v].size() ; i ++ )
if( g[v][i]->other(v) == w )
return true;
return false;
}
// 显示图的信息
void show(){
for( int i = 0 ; i < n ; i ++ ){
cout<<"vertex "<<i<<":\t";
for( int j = 0 ; j < g[i].size() ; j ++ )
cout<<"( to:"<<g[i][j]->w()<<",wt:"<<g[i][j]->wt()<<")\t";
cout<<endl;
}
}
// 邻边迭代器, 传入一个图和一个顶点,
// 迭代在这个图中和这个顶点向连的所有边
class adjIterator{
private:
SparseGraph &G; // 图G的引用
int v;
int index;
public:
// 构造函数
adjIterator(SparseGraph &graph, int v): G(graph){
this->v = v;
this->index = 0;
}
~adjIterator(){}
// 返回图G中与顶点v相连接的第一个边
Edge<Weight>* begin(){
index = 0;
if( G.g[v].size() )
return G.g[v][index];
// 若没有顶点和v相连接, 则返回NULL
return NULL;
}
// 返回图G中与顶点v相连接的下一个边
Edge<Weight>* next(){
index += 1;
if( index < G.g[v].size() )
return G.g[v][index];
return NULL;
}
// 查看是否已经迭代完了图G中与顶点v相连接的所有顶点
bool end(){
return index >= G.g[v].size();
}
};
};
#endif //INC_01_WEIGHTED_GRAPH_SPARSEGRAPH_H
DenseGraph.h
#ifndef INC_01_WEIGHTED_GRAPH_DENSEGRAPH_H
#define INC_01_WEIGHTED_GRAPH_DENSEGRAPH_H
#include <iostream>
#include <vector>
#include <cassert>
#include "Edge.h"
using namespace std;
// 稠密图 - 邻接矩阵
template <typename Weight>
class DenseGraph{
private:
int n, m; // 节点数和边数
bool directed; // 是否为有向图
vector<vector<Edge<Weight> *>> g; // 图的具体数据
public:
// 构造函数
DenseGraph( int n , bool directed){
assert( n >= 0 );
this->n = n;
this->m = 0;
this->directed = directed;
// g初始化为n*n的矩阵, 每一个g[i][j]指向一个边的信息, 初始化为NULL
g = vector<vector<Edge<Weight> *>>(n, vector<Edge<Weight> *>(n, NULL));
}
// 析构函数
~DenseGraph(){
for( int i = 0 ; i < n ; i ++ )
for( int j = 0 ; j < n ; j ++ )
if( g[i][j] != NULL )
delete g[i][j];
}
int V(){ return n;} // 返回节点个数
int E(){ return m;} // 返回边的个数
// 向图中添加一个边, 权值为weight
void addEdge( int v, int w , Weight weight ){
assert( v >= 0 && v < n );
assert( w >= 0 && w < n );
// 如果从v到w已经有边, 删除这条边
if( hasEdge( v , w ) ){
delete g[v][w];
if( v != w && !directed )
delete g[w][v];
m --;
}
g[v][w] = new Edge<Weight>(v, w, weight);
if( v != w && !directed )
g[w][v] = new Edge<Weight>(w, v, weight);
m ++;
}
// 验证图中是否有从v到w的边
bool hasEdge( int v , int w ){
assert( v >= 0 && v < n );
assert( w >= 0 && w < n );
return g[v][w] != NULL;
}
// 显示图的信息
void show(){
for( int i = 0 ; i < n ; i ++ ){
for( int j = 0 ; j < n ; j ++ )
if( g[i][j] )
cout<<g[i][j]->wt()<<"\t";
else
cout<<"NULL\t";
cout<<endl;
}
}
// 邻边迭代器, 传入一个图和一个顶点,
// 迭代在这个图中和这个顶点向连的所有边
class adjIterator{
private:
DenseGraph &G; // 图G的引用
int v;
int index;
public:
// 构造函数
adjIterator(DenseGraph &graph, int v): G(graph){
this->v = v;
this->index = -1; // 索引从-1开始, 因为每次遍历都需要调用一次next()
}
~adjIterator(){}
// 返回图G中与顶点v相连接的第一个边
Edge<Weight>* begin(){
// 索引从-1开始, 因为每次遍历都需要调用一次next()
index = -1;
return next();
}
// 返回图G中与顶点v相连接的下一个边
Edge<Weight>* next(){
// 从当前index开始向后搜索, 直到找到一个g[v][index]为true
for( index += 1 ; index < G.V() ; index ++ )
if( G.g[v][index] )
return G.g[v][index];
// 若没有顶点和v相连接, 则返回NULL
return NULL;
}
// 查看是否已经迭代完了图G中与顶点v相连接的所有边
bool end(){
return index >= G.V();
}
};
};
#endif //INC_01_WEIGHTED_GRAPH_DENSEGRAPH_H
ReadGraph.h
#ifndef INC_01_WEIGHTED_GRAPH_READGRAPH_H
#define INC_01_WEIGHTED_GRAPH_READGRAPH_H
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <cassert>
using namespace std;
// 读取有权图、
template <typename Graph, typename Weight>
class ReadGraph{
public:
// 从文件filename中读取有权图的信息, 存储进图graph中
ReadGraph(Graph &graph, const string &filename){
ifstream file(filename);
string line;
int V, E;
assert(file.is_open());
// 第一行读取图中的节点个数和边的个数
assert( getline(file,line));
stringstream ss(line);
ss >> V >> E;
assert( graph.V() == V );
// 读取每一条边的信息
for( int i = 0 ; i < E ; i ++ ){
assert( getline(file,line));
stringstream ss(line);
int a, b;
Weight w;
ss>>a>>b>>w;
assert( a >= 0 && a < V );
assert( b >= 0 && b < V );
graph.addEdge(a, b, w);
}
}
};
#endif //INC_01_WEIGHTED_GRAPH_READGRAPH_H
main.cpp测试
#include <iostream>
#include <iomanip>
#include "DenseGraph.h"
#include "SparseGraph.h"
#include "ReadGraph.h"
using namespace std;
// 测试有权图和有权图的读取
int main() {
string filename = "testG1.txt";
int V = 8;
cout<<fixed<<setprecision(2);
// Test Weighted Dense Graph
DenseGraph<double> g1 = DenseGraph<double>(V, false);
ReadGraph<DenseGraph<double>,double> readGraph1(g1, filename);
g1.show();
cout<<endl;
// Test Weighted Sparse Graph
SparseGraph<double> g2 = SparseGraph<double>(V, false);
ReadGraph<SparseGraph<double>,double> readGraph2(g2, filename);
g2.show();
cout<<endl;
return 0;
}
testG1.txt
8 16
4 5 .35
4 7 .37
5 7 .28
0 7 .16
1 5 .32
0 4 .38
2 3 .17
1 7 .19
0 2 .26
1 2 .36
1 3 .29
2 7 .34
6 2 .40
3 6 .52
6 0 .58
6 4 .93
最小生成树问题(带权图)![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/863908c22eb9d881cd3b0260dc04696f.png)
针对带权无向图, 针对连通图(如果不连通就求每个联通分量的最小生成树, 形成最小生成森林)
上面红色边就是最小生成书中的一条边
Lazy Prim
Prim是人名
使用最小堆存储上面绿色边
形成新的切分
0.34和0.36 已经不是最小生成树的候选了, 但是lazy prim不管
MinHeap.h
#ifndef INC_03_LAZY_PRIM_MINHEAP_H
#define INC_03_LAZY_PRIM_MINHEAP_H
#include <algorithm>
#include <cassert>
using namespace std;
// 最小堆
template<typename Item>
class MinHeap{
private:
Item *data;
int count;
int capacity;
void shiftUp(int k){
while( k > 1 && data[k/2] > data[k] ){
swap( data[k/2], data[k] );
k /= 2;
}
}
void shiftDown(int k){
while( 2*k <= count ){
int j = 2*k;
if( j+1 <= count && data[j+1] < data[j] ) j ++;
if( data[k] <= data[j] ) break;
swap( data[k] , data[j] );
k = j;
}
}
public:
// 构造函数, 构造一个空堆, 可容纳capacity个元素
MinHeap(int capacity){
data = new Item[capacity+1];
count = 0;
this->capacity = capacity;
}
// 构造函数, 通过一个给定数组创建一个最小堆
// 该构造堆的过程, 时间复杂度为O(n)
MinHeap(Item arr[], int n){
data = new Item[n+1];
capacity = n;
for( int i = 0 ; i < n ; i ++ )
data[i+1] = arr[i];
count = n;
for( int i = count/2 ; i >= 1 ; i -- )
shiftDown(i);
}
~MinHeap(){
delete[] data;
}
// 返回堆中的元素个数
int size(){
return count;
}
// 返回一个布尔值, 表示堆中是否为空
bool isEmpty(){
return count == 0;
}
// 向最小堆中插入一个新的元素 item
void insert(Item item){
assert( count + 1 <= capacity );
data[count+1] = item;
shiftUp(count+1);
count ++;
}
// 从最小堆中取出堆顶元素, 即堆中所存储的最小数据
Item extractMin(){
assert( count > 0 );
Item ret = data[1];
swap( data[1] , data[count] );
count --;
shiftDown(1);
return ret;
}
// 获取最小堆中的堆顶元素
Item getMin(){
assert( count > 0 );
return data[1];
}
};
#endif //INC_03_LAZY_PRIM_MINHEAP_H
Edge.h
#ifndef INC_03_LAZY_PRIM_EDGE_H
#define INC_03_LAZY_PRIM_EDGE_H
#include <iostream>
#include <cassert>
using namespace std;
// 边
template<typename Weight>
class Edge{
private:
int a,b; // 边的两个端点
Weight weight; // 边的权值
public:
// 构造函数
Edge(int a, int b, Weight weight){
this->a = a;
this->b = b;
this->weight = weight;
}
// 空的构造函数, 所有的成员变量都取默认值
Edge(){}
~Edge(){}
int v(){ return a;} // 返回第一个顶点
int w(){ return b;} // 返回第二个顶点
Weight wt(){ return weight;} // 返回权值
// 给定一个顶点, 返回另一个顶点
int other(int x){
assert( x == a || x == b );
return x == a ? b : a;
}
// 输出边的信息
friend ostream& operator<<(ostream &os, const Edge &e){
os<<e.a<<"-"<<e.b<<": "<<e.weight;
return os;
}
// 边的大小比较, 是对边的权值的大小比较
bool operator<(Edge<Weight>& e){
return weight < e.wt();
}
bool operator<=(Edge<Weight>& e){
return weight <= e.wt();
}
bool operator>(Edge<Weight>& e){
return weight > e.wt();
}
bool operator>=(Edge<Weight>& e){
return weight >= e.wt();
}
bool operator==(Edge<Weight>& e){
return weight == e.wt();
}
};
#endif //INC_03_LAZY_PRIM_EDGE_H
LazyPrimMST.h
#ifndef INC_03_LAZY_PRIM_LAZYPRIMMST_H
#define INC_03_LAZY_PRIM_LAZYPRIMMST_H
#include <iostream>
#include <vector>
#include <cassert>
#include "Edge.h"
#include "MinHeap.h"
using namespace std;
// 使用Prim算法求图的最小生成树
template<typename Graph, typename Weight>
class LazyPrimMST{
private:
Graph &G; // 图的引用
MinHeap<Edge<Weight>> pq; // 最小堆, 算法辅助数据结构
bool *marked; // 标记数组, 在算法运行过程中标记节点i是否被访问
vector<Edge<Weight>> mst; // 最小生成树所包含的所有边
Weight mstWeight; // 最小生成树的权值
// 访问节点v
void visit(int v){
assert( !marked[v] );
marked[v] = true;
// 将和节点v相连接的所有未访问的边放入最小堆中
typename Graph::adjIterator adj(G,v);
for( Edge<Weight>* e = adj.begin() ; !adj.end() ; e = adj.next() )
if( !marked[e->other(v)] )
pq.insert(*e);
}
public:
// 构造函数, 使用Prim算法求图的最小生成树
LazyPrimMST(Graph &graph):G(graph), pq(MinHeap<Edge<Weight>>(graph.E())){
// 算法初始化
marked = new bool[G.V()];
for( int i = 0 ; i < G.V() ; i ++ )
marked[i] = false;
mst.clear();
// Lazy Prim
visit(0);
while( !pq.isEmpty() ){
// 使用最小堆找出已经访问的边中权值最小的边
Edge<Weight> e = pq.extractMin();
// 如果这条边的两端都已经访问过了, 则扔掉这条边
if( marked[e.v()] == marked[e.w()] )
continue;
// 否则, 这条边则应该存在在最小生成树中
mst.push_back( e );
// 访问和这条边连接的还没有被访问过的节点
if( !marked[e.v()] )
visit( e.v() );
else
visit( e.w() );
}
// 计算最小生成树的权值
mstWeight = mst[0].wt();
for( int i = 1 ; i < mst.size() ; i ++ )
mstWeight += mst[i].wt();
}
// 析构函数
~LazyPrimMST(){
delete[] marked;
}
// 返回最小生成树的所有边
vector<Edge<Weight>> mstEdges(){
return mst;
};
// 返回最小生成树的权值
Weight result(){
return mstWeight;
};
};
#endif //INC_03_LAZY_PRIM_LAZYPRIMMST_H
DenseGraph.h
#ifndef INC_03_LAZY_PRIM_DENSEGRAPH_H
#define INC_03_LAZY_PRIM_DENSEGRAPH_H
#include <iostream>
#include <vector>
#include <cassert>
#include "Edge.h"
using namespace std;
// 稠密图 - 邻接矩阵
template <typename Weight>
class DenseGraph{
private:
int n, m; // 节点数和边数
bool directed; // 是否为有向图
vector<vector<Edge<Weight> *>> g; // 图的具体数据
public:
// 构造函数
DenseGraph( int n , bool directed){
assert( n >= 0 );
this->n = n;
this->m = 0;
this->directed = directed;
// g初始化为n*n的矩阵, 每一个g[i][j]指向一个边的信息, 初始化为NULL
g = vector<vector<Edge<Weight> *>>(n, vector<Edge<Weight> *>(n, NULL));
}
// 析构函数
~DenseGraph(){
for( int i = 0 ; i < n ; i ++ )
for( int j = 0 ; j < n ; j ++ )
if( g[i][j] != NULL )
delete g[i][j];
}
int V(){ return n;} // 返回节点个数
int E(){ return m;} // 返回边的个数
// 向图中添加一个边, 权值为weight
void addEdge( int v, int w , Weight weight ){
assert( v >= 0 && v < n );
assert( w >= 0 && w < n );
// 如果从v到w已经有边, 删除这条边
if( hasEdge( v , w ) ){
delete g[v][w];
if( v != w && !directed )
delete g[w][v];
m --;
}
g[v][w] = new Edge<Weight>(v, w, weight);
if( v != w && !directed )
g[w][v] = new Edge<Weight>(w, v, weight);
m ++;
}
// 验证图中是否有从v到w的边
bool hasEdge( int v , int w ){
assert( v >= 0 && v < n );
assert( w >= 0 && w < n );
return g[v][w] != NULL;
}
// 显示图的信息
void show(){
for( int i = 0 ; i < n ; i ++ ){
for( int j = 0 ; j < n ; j ++ )
if( g[i][j] )
cout<<g[i][j]->wt()<<"\t";
else
cout<<"NULL\t";
cout<<endl;
}
}
// 邻边迭代器, 传入一个图和一个顶点,
// 迭代在这个图中和这个顶点向连的所有边
class adjIterator{
private:
DenseGraph &G; // 图G的引用
int v;
int index;
public:
// 构造函数
adjIterator(DenseGraph &graph, int v): G(graph){
this->v = v;
this->index = -1; // 索引从-1开始, 因为每次遍历都需要调用一次next()
}
~adjIterator(){}
// 返回图G中与顶点v相连接的第一个边
Edge<Weight>* begin(){
// 索引从-1开始, 因为每次遍历都需要调用一次next()
index = -1;
return next();
}
// 返回图G中与顶点v相连接的下一个边
Edge<Weight>* next(){
// 从当前index开始向后搜索, 直到找到一个g[v][index]为true
for( index += 1 ; index < G.V() ; index ++ )
if( G.g[v][index] )
return G.g[v][index];
// 若没有顶点和v相连接, 则返回NULL
return NULL;
}
// 查看是否已经迭代完了图G中与顶点v相连接的所有边
bool end(){
return index >= G.V();
}
};
};
#endif //INC_03_LAZY_PRIM_DENSEGRAPH_H
SparseGraph.h
#ifndef INC_03_LAZY_PRIM_SPARSEGRAPH_H
#define INC_03_LAZY_PRIM_SPARSEGRAPH_H
#include <iostream>
#include <vector>
#include <cassert>
#include "Edge.h"
using namespace std;
// 稀疏图 - 邻接表
template<typename Weight>
class SparseGraph{
private:
int n, m; // 节点数和边数
bool directed; // 是否为有向图
vector<vector<Edge<Weight> *> > g; // 图的具体数据
public:
// 构造函数
SparseGraph( int n , bool directed){
assert(n >= 0);
this->n = n;
this->m = 0; // 初始化没有任何边
this->directed = directed;
// g初始化为n个空的vector, 表示每一个g[i]都为空, 即没有任和边
g = vector<vector<Edge<Weight> *> >(n, vector<Edge<Weight> *>());
}
// 析构函数
~SparseGraph(){
for( int i = 0 ; i < n ; i ++ )
for( int j = 0 ; j < g[i].size() ; j ++ )
delete g[i][j];
}
int V(){ return n;} // 返回节点个数
int E(){ return m;} // 返回边的个数
// 向图中添加一个边, 权值为weight
void addEdge( int v, int w , Weight weight){
assert( v >= 0 && v < n );
assert( w >= 0 && w < n );
// 注意, 由于在邻接表的情况, 查找是否有重边需要遍历整个链表
// 我们的程序允许重边的出现
g[v].push_back(new Edge<Weight>(v, w, weight));
if( v != w && !directed )
g[w].push_back(new Edge<Weight>(w, v, weight));
m ++;
}
// 验证图中是否有从v到w的边
bool hasEdge( int v , int w ){
assert( v >= 0 && v < n );
assert( w >= 0 && w < n );
for( int i = 0 ; i < g[v].size() ; i ++ )
if( g[v][i]->other(v) == w )
return true;
return false;
}
// 显示图的信息
void show(){
for( int i = 0 ; i < n ; i ++ ){
cout<<"vertex "<<i<<":\t";
for( int j = 0 ; j < g[i].size() ; j ++ )
cout<<"( to:"<<g[i][j]->w()<<",wt:"<<g[i][j]->wt()<<")\t";
cout<<endl;
}
}
// 邻边迭代器, 传入一个图和一个顶点,
// 迭代在这个图中和这个顶点向连的所有边
class adjIterator{
private:
SparseGraph &G; // 图G的引用
int v;
int index;
public:
// 构造函数
adjIterator(SparseGraph &graph, int v): G(graph){
this->v = v;
this->index = 0;
}
~adjIterator(){}
// 返回图G中与顶点v相连接的第一个边
Edge<Weight>* begin(){
index = 0;
if( G.g[v].size() )
return G.g[v][index];
// 若没有顶点和v相连接, 则返回NULL
return NULL;
}
// 返回图G中与顶点v相连接的下一个边
Edge<Weight>* next(){
index += 1;
if( index < G.g[v].size() )
return G.g[v][index];
return NULL;
}
// 查看是否已经迭代完了图G中与顶点v相连接的所有顶点
bool end(){
return index >= G.g[v].size();
}
};
};
#endif //INC_03_LAZY_PRIM_SPARSEGRAPH_H
ReadGraph.h
#ifndef INC_03_LAZY_PRIM_READGRAPH_H
#define INC_03_LAZY_PRIM_READGRAPH_H
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <cassert>
using namespace std;
// 读取有权图、
template <typename Graph, typename Weight>
class ReadGraph{
public:
// 从文件filename中读取有权图的信息, 存储进图graph中
ReadGraph(Graph &graph, const string &filename){
ifstream file(filename);
string line;
int V, E;
assert(file.is_open());
// 第一行读取图中的节点个数和边的个数
assert( getline(file,line));
stringstream ss(line);
ss >> V >> E;
assert( graph.V() == V );
// 读取每一条边的信息
for( int i = 0 ; i < E ; i ++ ){
assert( getline(file,line));
stringstream ss(line);
int a, b;
Weight w;
ss>>a>>b>>w;
assert( a >= 0 && a < V );
assert( b >= 0 && b < V );
graph.addEdge(a, b, w);
}
}
};
#endif //INC_03_LAZY_PRIM_READGRAPH_H
#include <iostream>
#include <iomanip>
#include "DenseGraph.h"
#include "SparseGraph.h"
#include "ReadGraph.h"
#include "LazyPrimMST.h"
using namespace std;
// 测试最小生成树算法
int main() {
string filename = "testG1.txt";
int V = 8;
SparseGraph<double> g = SparseGraph<double>(V, false);
ReadGraph<SparseGraph<double>, double> readGraph(g, filename);
// Test Lazy Prim MST
cout<<"Test Lazy Prim MST:"<<endl;
LazyPrimMST<SparseGraph<double>, double> lazyPrimMST(g);
vector<Edge<double>> mst = lazyPrimMST.mstEdges();
for( int i = 0 ; i < mst.size() ; i ++ )
cout<<mst[i]<<endl;
cout<<"The MST weight is: "<<lazyPrimMST.result()<<endl;
cout<<endl;
return 0;
}
kruskal
每次都找最短的那条边, 这条边就是最小生成树的一条边, 我们总能找到一条切分是的这个横切边是最短边
先对所有权重进行排序
0-7 这个edge是未访问中最小的, 只要没生成环就行
下一个最小的边是 2-3
1-7 没生成环
0-2也是最小生成树的一条边
5-7也是
1-3 // 1-5 // 2-7 都会形成环不是最小生成树的一条edge
2021年408真题11
最小生成树问题