堆的用法总结

2023-11-02

堆(heaps)

不是容器,而是一种特别的数据组织方式。堆一般用来保存序列容器。

堆很重要,很多不同的计算机进程中都使用了它们。为了弄明白堆是什么,首先需要明白树是什么,因此首先说明树这种数据结构是什么。

树是分层排列的元素或节点。每个节点有一个键,它是节点中所保存的对象,就如同链表中的节点。父节点是有一个或两个子节点的节点。一般父节点可以有任意个数的子节点,树中的父节点不需要有相同个数的子节点。没有子节点的节点叫作叶节点。一般父节点的键与其子节点有一些关系。树都有一个根节点,它是树的基础,从根节点可以到达所有的子节点。
图 1 二叉树示例
图 1 展示了一棵树,它表示 2014 年世界杯最后一组比赛的结果。德国全部赢了,所以它是根节点;它在最后一场比赛中打败了巴西队,所以它和巴西队是它自己的子节点。每个父节点最多有两个子节点的树叫作二叉树。

图 1 中的树是一个完全二叉树,因为每个父节点都有两个子节点。任何树的父节点都有指向子节点的指针。完全二叉树可以用数组的方式保存,也可以用其他顺序表的方式保存,例如 vector,这样就不需要保存子节点的指针,因为知道每一层节点的编号。

如果将每一层树的层数记作 n,从根节点开始作为第 0 层,每一层包含 2n 个节点。图 1 展示了世界杯比赛树的节点如何存储在数组中。每个节点上的整数值是索引值。根节点存放在数组的第一个元素中,后面是它的两个子节点。这对子节点的孩子节点出现在序列的下个位置,以此类推直到叶节点。子节点的索引值为 n,那么它的父节点的索引值就为 (n-1)/2。如果数组元素从 1 开始索引,那么父节点的索引表达式更加简单,它为 n/2。

现在可以定义一个堆 :这个堆是一个完全二叉树,每个节点与其子节点位置相对。父节点总是大于或等于子节点,这种情况下被叫作大顶堆,或者父节点总是小于或等于子节点,这种情况下叫作小顶堆。 注意,给定父节点的子节点不一定按顺序排列。

创建堆

用来创建堆的函数定义在头文件 algorithm 中。max_heap() 对随机访问迭代器指定的一段元素重新排列,生成一个堆。默认使用的是 < 运算符,可以生成一个大顶堆。例如:

std::vector<double>numbers{2.5,10.0,3.5,6.5,8.0,12.0,1.5,6.0};
std::make_heap(std::begin(numbers), std:rend(numbers));
// Result: 12 10 3.5 6.5 8 2.5 1.5 6

调用 make_heap() 后,vector 中的元素如注释所示,这也说明了图 2 所展示的结构。
在这里插入图片描述

根节点是 12,10 和 3.5 是它的子节点。10 的子节点是 6.5 和 8,3.5 的子节点是 2.5 和 1.5。6.5 只有一个叶节点 6。

priority_queue 是一个堆。在底层,一个 priority_queue 实例创建了一个堆。在堆中,所有成对的连续元素不需要有相同的比较关系。图 2 所示堆中的前 3 个元素是顺序递减的,但第 4 个元素却大于第 3 个元素。既然如此,为什么 STL 有 priority_queue (它是一个堆),却还需要创建堆,特别是还需要将堆作为优先级队列?

这是因为 priority_queue 可以提供堆没有的优势,它可以自动保持元素的顺序;但我们不能打乱 priority_queue 的有序状态,因为除了第一个元素,我们无法直接访问它的其他元素。如果需要的是一个优先级队列,这一点非常有用。

从另一方面来说,使用 make_heap() 创建的堆可以提供一些 priority_queue 没有的优势:

1、可以访问堆中的任意元素,而不限于最大的元素,因为元素被存储在一个容器中,就像是我们自己的vector。这也提供了偶然破坏元素顺序的可能,但是总可以调用 make_heap() 来还原堆。
2、可以在任何提供随机访问迭代器的序列容器中创建堆。这些序列容器包括普通数组、string对象、自定义容器。这意味着无论什么时候需要,都可以用这些序列容器的元素创建堆,必要时,可以反复创建。甚至还可以为元素的子集创建堆。

如果使用保持堆顺序的函数,那么可以将堆当作优先级队列使用。

这里有另一个版本的 make_heap(),它有第 3 个参数,可以用来指定一个比较函数用于堆的排序。通过定义一个大于运算符函数,可以生成一个小顶堆。这里可以使用 functional 中的断言。例如:

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers), std::end(numbers),
std::greater<>()); // Result: 1.5 6 2.5 6.5 8 12 3.5 10

可以将模板类型参数指定为 greater。这里的这个尖括号为空的版本推断并返回了类型参数。已经有一个用 make_heap() 函数在容器中生成的堆。可以在它上面进行很多操作,下面我们来深入了解这些操作。

堆操作

堆不是容器,而是组织容器元素的一种特别方式。 只能确定堆的范围,即开始和结束迭代器指定的范围。这意味着可以用容器中的元素子序列创建堆。可以在已生成的堆中添加元素。

乍一看,algorithm 中的函数模板 push_heap() 创建堆的方式可能会觉得有些奇怪。为了向堆中添加元素,首先可以用任何方法将元素附加到序列中。然后调用 push_heap() 来插入最后一个元素,为了保持堆的结构,这个元素会被重新排列到一个适当的位置。

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers),std::end(numbers));
// Result: 12 10 3.5 6.5 8 2.5 1.5 6
numbers.push_back(11); // Result: 12 10 3.5 6.5 8 2.5 1.5 6 11
std::push_heap(std::begin(numbers), std::end(numbers));
// Result: 12 11 3. 5 10 8 2. 5 1. 5 6 6. 5

注释显示了每个操作执行后的效果。必须以这种方式向堆中添加元素。只能通过调用成员函数向 queue 中添加新元素,而且这个成员函数只接受迭代器作为参数,不能直接以元素作为参数。

push_back() 会在序列末尾添加元素,然后使用 push_heap() 恢复堆的排序。通过调用 push_heap(),释放了一个信号,指出我们向堆中添加了一个元素,这可能会导致堆排序的混乱。push_heap() 会因此认为最后一个元素是新元素,为了保持堆结构,会重新排列序列。

从上面这个示例可以看出,重新排列是有必要的。我们注意到,尽管这个序列是一个堆,但是它的元素并不完全是按降序排列。这清楚地表明,尽管优先级队列是一个堆,但堆元素的顺序并不一定要和优先级队列相同。

当然,也可以用自己的比较函数来创建堆,但是必须和 push_heap() 使用相同的比较函数:

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers), std::end(numbers),
std::greater<>());//Result: 1.5 6 2.5 6.5 8 12 3.5 10 numbers. push—back(1. 2);
//Result: 1.5 6 2.5 6.5 8 12 3.5 10 1.2
std::push_heap(std::begin(numbers), std::end(numbers),std::greater<>());
//Result: 1.2 1.5 2.5 6 8 12 3.5 10 6.5

如果 push_heap() 和 make_heap() 的第 3 个参数不同,代码就无法正常执行。注释显示的结果中,最后的 6.5 似乎有些奇怪,图 3 展示的堆树能说明这个问题。
在这里插入图片描述
从树来看,显然 6.5 是 6(而不是 10)的子节点,所以这个堆结构是正确的。

删除最大元素和添加元素到堆的过程有些相似,但所做的事是相反的。首先调用 pop_heap(),然后从容器中移除最大的元素,例如:

std::vector<double> numbers{2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers),std::end(numbers));
//Result:12 10 3.5 6.5 8 2.5 1.5 6
std::pop_heap(std::begin(numbers),std::end(numbers));
// Result:10 8 3.5 6.5 6 2.5 1.5 12
numbers.pop_back();// Result:10 8 3.5 6.5 6 2.5 1.5

pop_heap() 函数将第一个元素移到最后,并保证剩下的元素仍然是一个堆。然后就可以使用 vector 的成员函数 pop_back() 移除最后一个元素。如果 make_heap() 中用的是自己的比较函数,那么 pop_heap() 的第 3 个参数也需要是这个函数:

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers),std::end(numbers),std::greater<>());
// Result: 1.5 6 2.5 6.5 8 12 3.5 10
std::pop_heap(std::begin(numbers), std::end(numbers),std:: greater<>());
// Result: 2.5 6 3.5 6.5 8 12 10 1.5
numbers.pop_back();//Result: 2.5 6 3.5 6.5 8 12 10

从注释显示的操作结果来看,显然需要为 pop_heap() 提供一个比较运算符函数。pop_heap() 函数不会交换第一个元素和最后一个元素,它会对从 begin(numbers) 到 end(numbers)-1 这个范围内的元素重新排序,从而保持堆的顺序。为了能够正确执行这个操作,pop_heap() 必须和 make_heap() 使用相同的比较函数。

因为可能会打乱容器中的堆,所以 STL 提供了一个检查序列是否仍然是堆的方法:

if(std::is_heap(std::begin(numbers),std::end(numbers)))
    std::cout << "Great! We still have a heap.\n";
else
    std::cout << "oh bother! We messed up the heap.\n";

如果元素段是堆,那么 is_heap() 会返回 true。这里是用默认的比较断言 less<> 来检查元素顺序。如果这里使用的是用 greater<> 创建的堆,就会产生错误的结果。为了得到正确的结果,表达式需要写为:

std::is_heap(std::begin(numbers),std::end(numbers),std::greater<>())

甚至可以更深入地检查元素中是否有部分元素为堆。例如:

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers), ::end(numbers),std:: greater<>());
// Result: 1.5 6 2.5 6.5 8 12 3.5 10
std::pop_heap (std::begin (numbers) , std::end(numbers),std::greater<>());
// Result: 2.5 6 3.5 6.5 8 12 10 1.5
auto iter = std::is_heap_until(std::begin(numbers),std::end(numbers),std::greater<>());
if(iter != std::end(numbers))
    std::cout << "numbers is a heap up to "<<*iter<<std::endl;

is_heap_until() 函数返回一个迭代器,指向第一个不在堆内的元素。这个代码段会输出最后一个元素的值 1.5,因为在调用 pop_heap() 后,这个元素就不在堆内了。如果整段元素都是堆,函数会返回一个结束迭代器,因此if语句可以确保我们不会解引用一个结束迭代器。如果这段元素少于两个,也会返回一个结束迭代器。这里还有另一个版本的 is_heap_until(),它有两个参数,以 less<> 作为默认断言。

STL 提供的最后一个操作是 sort_heap(),它会将元素段作为堆来排序。如果元素段不是堆,程序会在运行时崩溃。这个函数有以两个迭代器为参数的版本,迭代器指向一个假定的大顶堆(用 less<> 排列),然后将堆中的元素排成降序。结果当然不再是大顶堆。下面是一个使用它的示例:

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers), std::end(numbers));
//Result: 12 10 3.5 6.5 8 2.5 1.5 6
std::sort_heap(std::begin(numbers), std::end(numbers));
// Result: 1.5 2.5 3.5 6 6.5 8 10 12

排序操作的结果不是一个大顶堆,而是一个小顶堆。如图 4 所示,尽管堆并不是全部有序的,但任何全部有序的序列都是堆。
在这里插入图片描述
第 2 个版本的 sort_heap() 有第 3 个参数,可以指定一个用来创建堆的断言。如果用断言 greater() 来创建堆,会生成一个小顶堆,对它进行排序会生成一个降序序列。排序后的序列不是小顶堆。下面的代码对此做了展示:

std::vector<double> numbers {2.5, 10.0, 3.5, 6.5, 8.0, 12.0, 1.5, 6.0};
std::make_heap(std::begin(numbers), std::end(numbers),std::greater<>());
// Result: 1.5 6 2.5 6.5 8 12 3.5 10
std::sort_heap(std::begin(numbers), std::end(numbers),std::greater<>());
// Result: 12 10 8 6.5 6 3.5 2.5 1.5

如最后一行注释中显示的那样,对小顶堆执行 sort_heap() 后,会变成一个大顶堆。

我们知道可以用定义在 algorithm 头文件中的函数模板 sort() 来对堆排序,那么为什么还需要 sort_heap() 函数?sort_heap() 函数可以使用特殊的排序算法,巧合的是它被叫作堆排序。这个算法首先会创建一个堆,然后充分利用数据的局部有序性对数据进行排序。sort_heap 认为堆总是存在的,所以它只做上面的第二步操作。充分利用堆的局部有序性可以潜在地使排序变得更快,尽管这可能并不是一直有用。

通过修改代码,我们可以用堆作为优先级队列:

// Using a heap as a priority queue
#include <iostream>                              // For standard streams
#include <iomanip>                               // For  stream manipulators
#include <algorithm>                             // For heap support functions
#include <string>                                // For string class
#include <deque>                                 // For deque container
using std::string;
// List a deque of words
void show(const std::deque<string>& words, size_t count = 5)
{
    if(words.empty()) return;                     // Ensure deque has elements
    // Find length of longest string
    auto max_len = std::max_element(std::begin(words), std::end(words),
                                  [](const string& s1, const string& s2)
                                  {return s1.size() < s2.size(); })->size();
    size_t n {count};
    for(const auto& word : words)
    {
        std::cout << std::setw(max_len + 1) << word << " ";
        if(--n) continue;
        std::cout << std::endl;
        n = count;
    }
    std::cout << std::endl;
}
int main()
{
    std::deque<string> words;
    std::string word;
    std::cout << "Enter words separated by spaces, enter Ctrl+Z on a separate line to end:\n";
    while (true)
    {
        if ((std::cin >> word).eof())
        {
            std::cin.clear();
            break;
        }
        words.push_back(word);
    }
    std::cout << "The words in the list are:" << std::endl;
    show(words);
    std::make_heap(std::begin(words), std::end(words));
    std::cout << "\nAfter making a heap, the words in the list are:" << std::endl;
    show(words);
    std::cout << "\nYou entered " << words.size() << " words. Enter some more:" << std::endl;
    while (true)
    {
        if ((std::cin >> word).eof())
        {
            std::cin.clear();
            break;
        }
        words.push_back(word);
        std::push_heap(std::begin(words), std::end(words));
    }
    std::cout << "\nThe words in the list are now:" << std::endl;
    show(words);
}

运行结果为:

Enter words separated by spaces, enter Ctrl+Z on a separate line to end:
one two three four five six seven
^Z
The words in the list are:
   one    two  three   four   five
   six  seven

After making a heap, the words in the list are:
   two    one  three   four   five
   six  seven

You entered 7 words. Enter some more:
eight nine ten twelve fifteen ninety forty fifty-three
^Z

The words in the list are now:
         two       twelve        three         nine          ten
         six        seven        eight         four         five
         one      fifteen       ninety        forty  fifty-three

这个示例在一个 deque 容器中创建了一个堆,这和之前的示例不同;这里也可以使用 vector 容器。show() 函数可以列出 deque 容器中的所有单词。为了能够整齐地输出,单词都以比最大单词长度长 1 的固定宽度输出。可以使用定义在 algorithm 头文件中的 max_element() 函数来计算单词最大长度。

通过使用提供的比较函数,max_element() 会返回一个指向最大元素的迭代器。前两个参数是指定序列范围的迭代器。第 3 个参数是一个用于比较运算的 lambda 表达式。

注意,max_dement() 函数需要定义小于而不是大于运算,用来查找最大元素。比较函数的形式如下:
bool comp(const T1& a,const T2& b);
大多数情况下,第一个参数和第二个参数的类型相同,但有时类型也可以不同。唯一的要求是,这个范围内的元素需要可以隐式转换为 T1、T2 类型。参数不需要指定为 const,但最好这样做。在任何情况下,比较函数都不能改变传给它的参数值。

lambda 表达式可以返回字符串的 size() 值的比较结果。max_element() 返回的迭代器指向最长的字符串,因此可以调用它的成员函数 size() 来将它的长度记录到 max_len 中。

用我们之前见过的方式从 cin 中读取单词。这里调用 cin 的成员函数 clear() 来清除 EOF 状态,这个状态是在输入 Ctrl+Z 时设置的。如果不调用 clear(),EOF 状态会继续保留,这样后面就无法再从标准输入流获取输入了。

读入一些单词序列后,通过调用 make_heap() 函数将 deque 容器中的内容排成堆。然后读取一些单词,在将每个单词添加到容器时,需要调用 push_heap() 来保持堆序。push_heap() 希望新元素被添加在容器的尾部;如果使用 push_front(),程序会因此崩溃,因为这时候堆是无效的。输出表明所有代码按预期工作。

当然,如果每次输入单词后,都使用 push_heap(),就不需要调用 make_heap()。该例展示了如何使用我们控制的底层容器来访问全部元素,并且保留它们,而不需要像使用优先级队列那样在使用前不得不先备份它。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

堆的用法总结 的相关文章

  • 深入理解C++中的mutable关键字

    mutalbe的中文意思是 可变的 易变的 跟constant 既C 中的const 是反义词 在C 中 mutable也是为了突破const的限制而设置的 被mutable修饰的变量 将永远处于可变的状态 即使在一个const函数中 我们
  • C++中类型转化

    对象类型向上向下转化 对象转化是对象间的按成员赋值 有新的存储被分配 有新对象产生 类型指针向上向下转化 转化后没有得到引得对象 也可以说没有新的存储分配 我们只是通过一个小的 转化后的对象的大小 视角去看待这个对象 因此在诸如对象的释 放
  • Win32 Application 、Win32 Console Application、MFC工程项目

    Win32 Application和Win32 Console Application 都是工作在32位Windows环境的程序 Win32 Application 是标准 windows程序 完全拥有windows的特性 也即我们常说的窗
  • 转:C语言头文件下包含函数(math.h stdio.h stdlib.h string.h)

    math h常用函数 int abs int x double acos double x double asin double x double atan double x double atan2 double y double x d
  • 将摄像头输出的原始数据文件转换成bmp图像

    引言 从摄像头和传感器获得的视频数据是没有办法直接被电脑识别的 所以需要进行转化 我昨天在做项目的时候遇到了这个问题 根据查阅相关资料 实现了将摄像头输出的原始数据文件转换成bmp图像的程序 语言 C C 测试平台 VC6 0 先把bmp
  • C++字符指针的特殊

    如果我们对一个非字符的指针进行操作 方法是这样的 注意 int p 则p i 等价于 p i 定义 1 int a 7 int p a 或者 2 int a 7 int p p a 或者 3 int a 7 int p p a 1 这样定义
  • C语言变参函数解析

    1 函数声明 首先 要实现类似printf 的变参函数 函数的最后一个参数要用 表示 如 int log char arg1 这样编译器才能知道这个函数是变参函数 这个参数与变参函数的内部实现完全没有关系 只是让编译器在编译调用此类函数的语
  • C语言头插法创建双链表

    1 问题描述 使用头插法创建双链表 2 与单链表不同的是 双链表的结构体中多了一个变量就是指向当前节点的前驱节点 这样我们在循环遍历的时候可以通过当前节点的前驱指针找到前驱节点 在创建双链表的时候比单链表多了一个步骤就是对于前驱指针的操作
  • 将一个链表分为奇偶两个链表

    1 问题描述 设计一个算法 将一个头结点为A的单链表 其数据域为整数 分解成两个单链表A和B 使得A链表只含有原来链表data域为奇数的节点 而B链表只含有原链表中data域为偶数的节点 而且保持原来的顺序 2 思路分析 这个问题不是在线网
  • C中字符串操作

    字符串可以看作一个数组 它的每个元素是字符型的 例如字符串 Hello world n 图示如下 H e l l o w o r l d n 0 15个字符 注意每个字符串末尾都有一个字符 0 做结束符 这里的 0是ASCII码的八进制表示
  • 转:《C++ Templates》读书笔记

    有三种模板参数 形参 1 类型参数 这是使用得最多的 2 非类型参数 3 模板的模板参数 类型参数 类型参数是通过关键字typename或者class引入 关键字后面必须是一个简单的标识符 后面用逗号来隔开下一个参数声明 等号代表接下来的是
  • BMP文件格式详解(BMP file format)

    BMP文件格式 又称为Bitmap 位图 或是DIB Device Independent Device 设备无关位图 是Windows系统中广泛使用的图像文件格式 由于它可以不作任何变换地保存图像像素域的数据 因此成为我们取得RAW数据的
  • C++基本语法

    C 中构造函数的调用 class Type void main Type obj ok 编译器调用了默认的构造函数 Type obj1 Type ok 显示调用了默认的构造函数 Type obj3 error 其是声明了一个无参并返回Typ
  • 指针与const限定符

    const限定符和指针结合起来常见的情况有以下几种 const int a int const a 这两种写法是一样的 a是一个指向const int的指针 a所指向的内存单元不可改写 所以 a 是不允许的 但a可以改写 所以a 是允许的
  • C语言中的printf与scanf函数

    1 printf函数的格式字符串 md 左对齐 若m比实际少时 按实际输出 不足右补空格 include
  • C语言中的.c 和.h 文件区别

    本文的大部分内容来自网上 经过自己的理解和总结整理而来 希望对和自己有同样的困惑的人予以参考 感谢那些网上的大牛们 是你们的无私 让我学到了很多的东西 予人玫瑰 手留余香 文章转载请注明出处 首先我们先看一下这个例子 由于我也不知道到底那个
  • bmp文件

    文件格式 格式组成 典型的BMP 图像文件由四部分组成 1 位图头文件数据结构 它包含BMP图像文件的类型 显示内容等信息 2 位图信息数据结构 它包含有BMP图像的宽 高 压缩方法 以及定义颜色等信息 3 调色板 这个部分是可选的 有些位
  • printf()函数

    printf函数对输出表中各量求值的顺序是自右至左进行的 也即程序执行的过程中参数的压栈顺序是从右至左的 并且压栈时压入的是值 因为参数的压栈是在程序的执行过程中 所以即使参数列表中有函数调用则在压栈时也即计算出来 即调用此函数去执行 把得
  • 注册ActiveX控件的几种方法

    使用ActiveX控件可快速实现小型的组件重用 代码共享 从而提高编程效率 降低开发成本 但是ActiveX控件对于最终用户并不能直接使用 因为ActiveX控件必须先在Windows中注册 注册ActiveX控件一般来说有六种途径 它们有
  • VC6.0向工程中添加文件和打开文件出错“"0x5003eaed"指令引用的"0x00000000"内存”解

    据说这个错误是因为和微软的其他软件相冲突了 下面就看看如何解决这个问题 第一步 下载一个FileTool插件 下载的地址 http download microsoft com download vc60ent s1 6 0 w9xnt4

随机推荐

  • React使用antd实现可编辑单元格

    import React useContext useState useEffect useRef from react import Input Form from antd import Table from com Table imp
  • Flutter中的方法回调备忘

    类似于Android中的Callback iOS中的block 大致思路是一样的 需要自定义一个函数或者使用官方自己的也行 直接上代码 先写一个按钮点击事件 然后监听点击事件 备忘 onPress 和 onPress 特别需要注意 在wid
  • mysqld: Can‘t read dir of ‘/etc/mysql/conf.d/‘ (Errcode: 13 - Permission denied)

    今天用docker去运行mysql的时候 一直existing 输入 docker logs 镜像ID的时候发现报了mysqld Can t read dir of etc mysql conf d Errcode 13 Permissio
  • X-CSRF-Token

    Odata服务HTTP测试总是出现烦人的 CSRF token validation failed for all modifying requests 忽略下图中的报文错误 怀疑是服务器参数的设置问题 临时应急的话可以先针对这个服务把CS
  • Tars- zipkin环境本地搭建

    该图片引用于它处 https blog csdn net u012394095 article details 94389644 1 下载opentracing cpp 客户端调用的代码 网址为 opentraceing cpp 注意要下稳
  • 离散数学期末复习

    第一章 命题逻辑 联结词 蕴涵的注意事项 公式的层次 单个命题公式为0公式 等值演算的公式 范式 1 简单合取式 简单析取式 2 极小项 由简单合取式构成 m0 极大项 由简单析取式 构成 M0 奎因 莫可拉斯基方法求最简展开式 1 找极小
  • C语言笔记(二)

    基础 1 进制问题 1 1 二进制 1 2 ASCII 1 3 k进制转换为十进制 1 4 十进制转换为k进制 2 输入输出 3 逻辑运算符 4 运算符优先级 5 switch分支语句 6 字符串查找strchr函数 1 进制问题 1 1
  • 工作和生活中,如何用项目管理思维解决复杂的事情?

    在工作和生活中 许多事情都可以采用项目思维方式来解决 当我们逐渐将工作和生活中的各种事务以项目的方式来处理和推进时 我们可能并没有意识到 实际上我们正在运用项目管理思维 项目管理思维能帮助我们在面对繁杂事务时 理清思路 考虑周全 明确行动
  • 《程序员的自我修养—链接、装载与库》

    程序员的自我修养 链接 装载与库 读书笔记 本文为记录笔记 大部分内容为书中的摘抄 作者微博 MTK 蛙蛙鱼 写作时间 2013年11月18日 更新时间 2014年02月18日 编译和链接 2 1 被隐藏了的过程 预编译 cpp or gc
  • 蓝桥BASIC-18 矩形面积交 思路分析

    问题描述 平面上有两个矩形 它们的边平行于直角坐标系的X轴或Y轴 对于每个矩形 我们给出它的一对相对顶点的坐标 请你编程算出两个矩形的交的面积 输入格式 输入仅包含两行 每行描述一个矩形 在每行中 给出矩形的一对相对顶点的坐标 每个点的坐标
  • Qt开发上位机软件建立经典蓝牙通讯

    Qt开发上位机软件建立经典蓝牙通讯 之前做了一个具有经典蓝牙通讯功能的Windows上位机软件 在网上学习了相关博客以及参考了官方经典蓝牙例程之后 总结出了使用Qt建立经典蓝牙通讯的步骤 附带相关源码 作为分享 开发环境 我使用的Qt版本是
  • ESP32 LVGL开发一 移植与例程

    简介 LVGL 轻量级和通用图形库 是一个免费和开源的图形库 提供UI通信元素的构建接口与较低资源实现的源码 适用于快速开发UI图形交互页面的应用 官方已经适配了ESP32硬件平台 库版本为v7 11 开箱即用 如有异议 欢迎留言指正 特性
  • java进制转换及算法

    本文主要讲各个进制转换的方法 进制转换 前言 一 说明 1 作用 2 本质 3 方法 4 场景 二 实例 1 字符串与16进制的互转 2 16进制字符串与byte数组互转 3 字符串与指定格式的byte数组互转 4 字符串与16进制互转 5
  • PC-Lint c/c++ 代码检查工具

    概述 PC Lint是GIMPEL SOFTWARE公司的一个产品 它是一个历史悠久 功能异常强劲的静态代码检测工具 它的使用历史可以追溯到计算机编程的远古时代 30多年以前 经过这么多年的发展 它不但能够监测出许多语法逻辑上的隐患 而且也
  • Linux - Ubuntu下安装node.js的方法

    1 Putty连接 安装Putty连接到Ubuntu 输入密码验证后进入Putty命令行控制台 1 1 查看Ubuntu版本 sudo uname m 如果显示i686 你安装了32位操作系统 如果显示 x86 64 你安装了64位操作系统
  • Redis的高级特性一览

    更多内容 欢迎关注微信公众号 全菜工程师小辉 公众号回复关键词 领取免费学习资料 应用场景 缓存系统 用于缓解数据库的高并发压力 计数器 使用Redis原子操作 用于社交网络的转发数 评论数 粉丝数 关注数等 排行榜 使用zset数据结构
  • ag-grid表格如何使用?

    1 自定义标题 tableHeaderCustom vue
  • 证件照片如何换背景底色,3个免费制作证件照的方法,简单易学

    在日常生活中 我们经常需要用到证件照 比如 找工作需要简历上附带有证件照 还有办理学生证 身份证也需要提交证件照 不同的平台有时候提交的要求 背景底色 大小等 也不一样 如果你不想每次都重拍 那么可以用一些工具 软件 在原来的照片上修改 也
  • smb协议详解和samba服务的配置

    理论部分 samba 基于smb协议使网络上的计算机能共享文件 samba的核心是smbd和nmbd两个守护进程 smbd 管理samba服务器上的共享目录 nmbd 进行netbios名解析 使客户端能浏览服务器的共享资源 协议端口 sm
  • 堆的用法总结

    堆 heaps 不是容器 而是一种特别的数据组织方式 堆一般用来保存序列容器 堆很重要 很多不同的计算机进程中都使用了它们 为了弄明白堆是什么 首先需要明白树是什么 因此首先说明树这种数据结构是什么 树是分层排列的元素或节点 每个节点有一个