Python部分
join()
Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串。
s1 = "-"
s2 = ""
seq = ("r", "u", "n", "o", "o", "b") # 字符串序列
print (s1.join( seq ))
print (s2.join( seq ))
输出:
r-u-n-o-o-b
runoob
sort()
compare = lambda x,y: 1 if x > y else -1
nums.sort(cmp=compare)
map()
map() 会根据提供的函数对指定序列做映射。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
>>> def square(x) : # 计算平方数
... return x ** 2
...
>>> map(square, [1,2,3,4,5]) # 计算列表各个元素的平方
<map object at 0x100d3d550> # 返回迭代器
>>> list(map(square, [1,2,3,4,5])) # 使用 list() 转换为列表
[1, 4, 9, 16, 25]
>>> list(map(lambda x: x ** 2, [1, 2, 3, 4, 5])) # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]
strip()
Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。
>>> strs = ' asd '
>>> print(strs.strip())
asd
>>> strs
' asd '
>>> strs = 'aaaaaabaaaaaa'
>>> print(strs.strip('a'))
b
sum()
求列表前k项和
l = [1,2,3,4,5,6,7,7,8,9,9,0,11]
sum(l[:k])
2020/8/2 添加:
Python在向CSV文件写中文时乱码的处理办法
今年在用python向csv文件写入时发现了中文乱码,如下为解决方案:
- 1.方案一:对字符串转换编码(太麻烦,不推荐)
import csv
f = open("xieru1.csv", 'wb')
writer = csv.writer(f)
# 需要写入的信息
data = ["帅哥", "男", "月薪100w", "身高180", "北京市", "单身"]
a = []
for i in data:
a.append(i.decode("utf-8").encode("gbk"))
writer.writerow(a) # 写入单行
f.close()
- 2.方法二:用codecs提供的open方法来指定打开的文件的语言编码,它会在读取的时候自动转换为内部unicode (推荐)
import csv
data = {'name':'篮球鞋','value':'999.00元','fee':'5元','sale':'888单'}
def write_data_to_csv(data):
csv_file = codecs.open('taobao_res.csv','a','gbk')
writer = csv.writer(csv_file)
writer.writerow([data['name'],data['value'],data['fee'],data['sale']])
csv_file.close()
Python代码高级用法
将一个三维列表中所有一维数据为a的元素合并,组成新的二维列表
lista = [item for item in array if item[0] == 'a']
对于一个列表,既要遍历索引又要遍历元素\
array = ['I', 'love', 'Python']
for i, element in enumerate(array):
array[i] = '%d: %s' % (i, seq[i])
其中,enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
>>>seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1)) # 下标从 1 开始
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
迭代器和生成器
什么是迭代器?
迭代器指的是可以使用next()方法来回调的对象,可以对可迭代对象使用iter()方法,将其转换为迭代器
迭代器的优势
优势
在构建迭代器时,不是将所有的元素一次性的加载,而是等调用next方法时返回元素,所以不需要考虑内存的问题。
什么是生成器?
生成器是一种高级迭代器,使得需要返回一系列元素的函数所需的代码更加的简单和高效(不像创建迭代器代码那般冗长)
优势
优点是延迟计算,一次返回一个结果,这样非常适用于大数据量的计算。但是,使用生成器必须要注意的一点是:生成器只能遍历一次。
生成器函数
生成器函数基于yield指令,可以暂停一个函数并返回中间结果。当需要一个将返回一个序列或在循环中执行的函数时,就可以使用生成器,因为当这些元素被传递到另一个函数中进行后续处理时,一次返回一个元素可以有效的提升整体性能。
import sys
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a #返回元素a,下次进入该函数时从此处开始执行
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
输出:
0 1 1 2 3 5 8 13 21 34 55
lambda表达式(匿名函数)
lambda表达式纯粹是为了编写简单函数而设计,起到了一个函数速写的作用,使得简单函数可以更加简洁的表示。
lambda和def的区别
lambda表达式可以省去定义函数的过程,让代码更加的简洁,适用于简单函数,编写处理更大业务的函数需要使用def定义。
lambda表达式常搭配map(), reduce(), filter()函数使用:
-
map(): map函数接受两个参数,一个是函数,一个是序列,其中,函数可以接收一个或者多个参数。map将传入的函数依次作用于序列中的每个元素,将结果作为新的列表返回。
#将一个列表中的数字转换为字符串 map(str, [1,2,3,4,5,6])
-
reduce():函数接收两个参数,一个是函数,另一个是序列,但是,函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)。
-
filter():该函数用于筛选,将传入的函数,依次作用于每个元素,然后根据函数的返回值是True还是False,决定是留下还是丢弃该元素。
accumulate()
accumulate函数的功能是对传进来的iterable对象逐个进行某个操作(默认是累加,如果传了某个function()就是应用此function()。
比如iterable=[1,2,3,4] 默认会先累加iterable 0~0(1), 然后0~1(1+2),最后0~3(1+2+3)
注意:accumulate函数返回是一个可迭代对象,可以用在for里面,而不是最后的累加结果,如果我们想要的是直接的结果则需要强制转化类型,比如转化成list,详细可以给下面的示例
test = [1,12,-5,-6,50,3]
a = accumulate(test)
b = list(a)
print(b)
输出:
[1, 13, 8, 2, 52, 55]
zip()
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
ord()
ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。
>>>ord('a')
97
>>> ord('b')
98
bisect — 数组二分查找算法
这个模块对有序列表提供了支持,使得他们可以在插入新数据仍然保持有序。对于长列表,如果其包含元素的比较操作十分昂贵的话,这可以是对更常见方法的改进。这个模块叫做 bisect 因为其使用了基本的二分(bisection)算法。源代码也可以作为很棒的算法示例(边界判断也做好啦!)
定义了以下函数:
bisect.bisect_left(a, x, lo=0, hi=len(a))
在 a 中找到 x 合适的插入点以维持有序。参数 lo 和 hi 可以被用于确定需要考虑的子集;默认情况下整个列表都会被使用。如果 x 已经在 a 里存在,那么插入点会在已存在元素之前(也就是左边)。如果 a 是列表(list)的话,返回值是可以被放在 list.insert() 的第一个参数的。
返回的插入点 i 可以将数组 a 分成两部分。左侧是 all(val < x for val in a[lo:i]) ,右侧是 all(val >= x for val in a[i:hi]) 。
bisect.bisect_right(a, x, lo=0, hi=len(a))
类似于 bisect_left(),但是返回的插入点是 a 中已存在元素 x 的右侧。
返回的插入点 i 可以将数组 a 分成两部分。左侧是 all(val <= x for val in a[lo:i]),右侧是 all(val > x for val in a[i:hi]) for the right side。
bisect.insort_left(a, x, lo=0, hi=len(a))
将 x 插入到一个有序序列 a 里,并维持其有序。如果 a 有序的话,这相当于 a.insert(bisect.bisect_left(a, x, lo, hi), x)。要注意搜索是 O(log n) 的,插入却是 O(n) 的。
bisect.insort_right(a, x, lo=0, hi=len(a))
类似于 insort_left(),但是把 x 插入到 a 中已存在元素 x 的右侧。
import bisect
test = [1,2,5,6,50,87]
print(bisect.bisect_left(test,40))
输出:
4
collections模块之Counter
Counter目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。
创建
下面的代码说明了Counter类创建的四种方法:
Counter类的创建
>>> c = Counter() # 创建一个空的Counter类
>>> c = Counter('gallahad') # 从一个可iterable对象(list、tuple、dict、字符串等)创建
>>> c = Counter({'a': 4, 'b': 2}) # 从一个字典对象创建
>>> c = Counter(a=4, b=2) # 从一组键值对创建
计数值的访问与缺失的键
>>> c = Counter("abcdefgab")
>>> c["a"]
2
>>> c["c"]
1
>>> c["h"]
0
计数器的更新(update和subtract)
可以使用一个iterable对象或者另一个Counter对象来更新键值。
计数器的更新包括增加和减少两种。其中,增加使用update()方法:
计数器的更新(update)
>>> c = Counter('which')
>>> c.update('witch') # 使用另一个iterable对象更新
>>> c['h']
3
>>> d = Counter('watch')
>>> c.update(d) # 使用另一个Counter对象更新
>>> c['h']
4
减少则使用subtract()方法:
计数器的更新(subtract)
>>> c = Counter('which')
>>> c.subtract('witch') # 使用另一个iterable对象更新
>>> c['h']
1
>>> d = Counter('watch')
>>> c.subtract(d) # 使用另一个Counter对象更新
>>> c['a']
-1
键的修改和删除
当计数值为0时,并不意味着元素被删除,删除元素应当使用del。
键的删除
>>> c = Counter("abcdcba")
>>> c
Counter({'a': 2, 'c': 2, 'b': 2, 'd': 1})
>>> c["b"] = 0
>>> c
Counter({'a': 2, 'c': 2, 'd': 1, 'b': 0})
>>> del c["a"]
>>> c
Counter({'c': 2, 'b': 2, 'd': 1})
elements()
返回一个迭代器。元素被重复了多少次,在该迭代器中就包含多少个该元素。元素排列无确定顺序,个数小于1的元素不被包含。
elements()方法
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
most_common([n])
返回一个TopN列表。如果n没有被指定,则返回所有元素。当多个元素计数值相同时,排列是无确定顺序的。
most_common()方法
>>> c = Counter('abracadabra')
>>> c.most_common()
[('a', 5), ('r', 2), ('b', 2), ('c', 1), ('d', 1)]
>>> c.most_common(3)
[('a', 5), ('r', 2), ('b', 2)]
算术和集合操作
+、-、&、|操作也可以用于Counter。其中&和|操作分别返回两个Counter对象各元素的最小值和最大值。需要注意的是,得到的Counter对象将删除小于1的元素。
Counter对象的算术和集合操作
>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # subtract(只保留正数计数的元素)
Counter({'a': 2})
>>> c & d # 交集: min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d # 并集: max(c[x], d[x])
Counter({'a': 3, 'b': 2})
其他常用操作
sum(c.values()) # 所有计数的总数
c.clear() # 重置Counter对象,注意不是删除
list(c) # 将c中的键转为列表
set(c) # 将c中的键转为set
dict(c) # 将c中的键值对转为字典
c.items() # 转为(elem, cnt)格式的列表
Counter(dict(list_of_pairs)) # 从(elem, cnt)格式的列表转换为Counter类对象
c.most_common()[:-n:-1] # 取出计数最少的n个元素
c += Counter() # 移除0和负值
C部分
2020/8/3更新
生成一定范围内的随机数
C实现:生成a~b范围内的随机数
//需要包含头文件#icnlude<time.h>
#include<time.h>
srand((unsigned)time(NULL));
int a = rand() % (b-a+1)+a;
Python实现:生成a~b范围内的随机数
import random
random_data = random.randint(a, b)
C++部分
cpp源文件和hpp头文件应该写些什么?
头文件(.h):
写类的声明(包括类里面的成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体实现。
头文件还可以定义:在编译的时候就已知道其值的const对象和inline 函数。在头文件中定义上述实体,是因为编译器需要它们的定义来产生代码。
源文件(.cpp):
源文件主要写实现头文件中已经声明的那些函数的具体代码。需要注意的是,开头必须#include一下实现的头文件,以及要用到的头文件。那么当你需要用到自己写的头文件中的类时,只需要#include进来就行了。
new和delete
new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。另外需要注意的是,new的使用格式,new出来的是一段空间(delete释放)的首地址。
new和delete的内部机制:
-
new:
1、当初始化一个对象时
new—>operator new—>malloc—>构造函数
2、当初始化若干个对象时
new[]—>operator new[]—>operator new—>malloc—>构造函数 -
delete:
当delete对象时调用顺序为:
1、当delete单个对象时:
delete—>析构函数—>operator delete—>free
2、当delete多个对象时:
delete[]—>析构函数—>operator delete[]—>operator delete—>free
//可以在new后面直接赋值
int *p = new int(3);
delete p;
//当new一个数组时,同样用一个指针接住数组的首地址
int *q = new int[3];
delete [] q;
C++类中this指针
我们可以把class理解为一种类型,是用户自定义的类型。用这个类型可以来声明一个对象,如Fruit apple。对象apple是Fruit类型,具有Fruit 的属性和方法。理解了这个,就好解释this了,apple里的this就是指向apple的指针。因此this 的类型应该是Fruit (指向Fruit类型的指针),而对其的解引用this就应该是一个Fruit类型的对象。
同时我们还有一些要注意:
- 一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。
- this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。任何对类成员的直接访问都被看成this的隐式使用。
- this的目的总是指向这个对象,所以this是一个常量指针,不允许改变this中保存的地址。
this指针的使用
- 在类的非静态成员函数中返回类对象本身的时候,直接使用 return *this;
- 是当参数与成员变量名相同时,如this->n = n ;(不能写成n = n)
#include<iostream.h>
class Point
{
int x, y;
public:
Point(int a, int b) { this->x=a; this->y=b;}
Void MovePoint( int a, int b){ this->x+=a; this->y+=b;}
Void print(){ cout<<"x="<<this->x<<"y="<<this->y<<endl;}
};
C++hpp文件注意事项
- 一般来说,.h里面只有声明,没有实现,而*.hpp里声明实现都有,后者可以减少.cpp的数量。
- .h里面可以有using namespace std,而*.hpp里则无
由于hpp本质上是作为.h被调用者include,所以当hpp文件中存在全局对象或者全局函数,而该hpp被多个调用者include时,将在链接时导致符号重定义错误。要避免这种情况,需要去除全局对象,将全局函数封装为类的静态方法。
C++二维数组作函数形参
/对于一个m行n列int元素的二维数组
//函数f的形参形式
f(int daytab[m][n]) {...}
//以下两种可以忽略行数
f(int daytab[][n]) {...}
f(int (*daytab)[n]) {...}
以指针的指针来表示二维数组,动态分配内存的形式:
#include <iostream>
#include <stdio.h>
void out(double **a,int m, int n)
{
int i, j;
double b=0.0;
for(i=0; i<m; i++)
{
for (j=0; j<n; j++)
{
a[i][j] = b;
b += 1.2;
printf("%5.1f",a[i][j]);
}
std::cout << std::endl;
}
}
int main(int argc, char * agrv)
{
int i, j, m=2, n=3;
double **a;
a = new double*[m];
for (i=0; i<m; i++)
a[i] = new double[n];
out(a,m,n);
return 1;
}
C++ STL
2020/10/29更新
STL 头文件
C++ 标准库的规定,所有标准头文件都不再有扩展名。以 <vector>
为例,此为无扩展名的形式,而 <vector.h>
为有扩展名的形式。
#include<iterator>
#include<functional>
#include<vector>
#include<deque>
#include<list>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<numeric>
#include<utility>
vector容器创建二维矩阵
创建 row 行, col 列默认值为 0 的二维数组
vector<vector<int>> martix(rows,vector<int> (cols,0));
accumulate()
头文件#include<numeric>
accumulate带有三个形参:头两个形参指定要累加的元素范围,第三个形参则是累加的初值。
求累加和:
accumulate函数将它的一个内部变量设置为指定的初始值,然后在此初值上累加输入范围内所有元素的值。accumulate算法返回累加的结果,其返回类型就是其第三个实参的类型。
double base = 10.4;
cout<<accumulate(nums.begin(),nums.end(),0)<<endl;
cout<<accumulate(nums.begin(),nums.end(),base)<<endl;
cout<<accumulate(nums.begin()+3,nums.end(),0)<<endl;
输出:
20
30.4
14
容器内字符串连接:
vector<string> strs {"hello ","world","!"," I l","ove You"};
cout<<accumulate(strs.begin(),strs.end(),string(""))<<endl;
输出:
hello world! I love You
自定义数据类型的处理:
对于自定义数据类型,我们就需要自己动手写一个回调函数来实现自定义数据的处理,然后让它作为accumulate()的第四个参数.
#include <vector>
#include <string>
using namespace std;
struct Grade
{
string name;
int grade;
};
int main()
{
Grade subject[3] = {
{ "English", 80 },
{ "Biology", 70 },
{ "History", 90 }
};
int sum = accumulate(subject, subject + 3, 0, [](int a, Grade b){return a + b.grade; });
cout << sum << endl;
system("pause");
return 0;
}
为什么fun的形参要有int a呢?我们看下accumulate()的函数原型:
template<typename _InputIterator, typename _Tp, typename _BinaryOperation>
inline _Tp
accumulate(_InputIterator __first, _InputIterator __last, _Tp __init,
_BinaryOperation __binary_op)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
__glibcxx_requires_valid_range(__first, __last);
for (; __first != __last; ++__first)
__init = __binary_op(__init, *__first);
return __init;
}
可以看到__binary_op(__init, *__first),函数是有两个参数的,第一个参数也就是accumulate()的第三个参数累加初始值__init,我们不妨修改一下累加初始值看下输出结果:
int sum = accumulate(subject, subject + 3, 1000, [](int a,Grade b){return a+b.grade; });
cout<<sum;
此时输出sum为1240,与我们所想一样
sort()函数
sort()函数默认是升序排序:
vector<int> nums{7,4,2,5,11,34,11,1,4,17,23,51,30,41};
sort(nums.begin(),nums.end());
输出:1 2 4 4 5 7 11 11 17 23 30 34 41 51
通过自定义函数进行排序:
使用mycmp()进行降序排序
bool mycmp(int a,int b){
return a > b;
}
vector<int> nums{7,4,2,5,11,34,11,1,4,17,23,51,30,41};
sort(nums.begin(),nums.end());
输出:51 41 34 30 23 17 11 11 7 5 4 4 2 1
通过重载进行排序
class mycomp2 {
public:
bool operator() (int i, int j) {
return (i < j);
}
};
可以直接进行比较greater< int >() 递减, less< int >() 递增
vector<int> nums{7,4,2,5,11,34,11,1,4,17,23,51,30,41};
sort(nums.begin(),nums.end(),greater<int>());
输出:51 41 34 30 23 17 11 11 7 5 4 4 2 1
通过自定义函数对结构体等进行排序
typedef struct student{
string name;
int age;
}student;
bool mycmp(student a,student b){
if (a.name > b.name)
{
return a.name < b.name;
}else if (a.name == b.name)
{
return a.age > b.age;
}
}
student a[3]={{"apple",67},{"limei",90},{"apple",90}};
sort(a,a+3,mycmp);
输出:
apple 90
apple 67
limei 90
Opencv部分
2020/8/12更新:
opencv中Mat数据类型ptr指针的应用
cv::Mat image = cv::Mat(400, 600, CV_8UC1); //宽400,长600
uchar * data00 = image.ptr<uchar>(0); //指向第1行第1个元素
uchar * data10 = image.ptr<uchar>(1); //指向第2行第1个元素
uchar * data01 = image.ptr<uchar>(0)[1];//指向第1行第2个元素
2020/8/13更新:
opencv常用数据类型
Vec
Vec:vec是一个模板类,主要用于数值向量。我们可以定义任何类型的向量和大量的组件:
Vec<double,19> myVector;
opencv中采用了宏定义:
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;
typedef Vec<short, 2> Vec2s;
...
typedef Vec<ushort, 2> Vec2w;
...
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<int, 6> Vec6i;
typedef Vec<int, 8> Vec8i;
...
Point
opencv中提供了点的模板类,分为2维点模板类Point_和3维点模板类Point3_。Point_通过2维图像平面中的x和y坐标确定点的位置,Point3_通过3维立体图像中的x、y、z坐标确定点的位置。对于点的坐标的类型可以是int、double、float类型
typedef Point_<int> Point2i;
typedef Point_<int64> Point2l;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
typedef Point2i Point; //Point和Point2i等价
实例化一个Point对象
Point point = Point(10, 8);
Vector
向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
Opencv–TermCriteria类
cv::TermCriteria类
定义迭代算法终止条件的类。
参数:
- type: 终止条件的类型,TermCriteria::Type之一。
- maxCount:要计算的最大迭代次数或元素。
- epsilon:迭代算法停止的期望精度或参数更改。
Opencv–Size使用
Mat img;
img = Mat(400,600,CV_8UC3);//400行600列,先行后列
img = Mat(cv::Szie(400,600),CV_8UC3);//600行400列,先列后行
Opencv坐标系
row == heigh == Point.y
col == width == Point.x
Mat::at(Point(x, y)) == Mat::at(y,x)
OpenCV Mat数据类型指针ptr
cv::Mat image = cv::Mat(400, 600, CV_8UC1); //宽400,长600
uchar * data00 = image.ptr<uchar>(0);
uchar * data10 = image.ptr<uchar>(1);
uchar * data01 = image.ptr<uchar>(0)[1];
注释:
- 定义了一个Mat变量image。
- data00是指向image第一行第一个元素的指针。
- data10是指向image第二行第一个元素的指针。
- data01是指向image第一行第二个元素的指针。
OpenCV applyColorMap()
假设我们想在地图上显示美国不同地区的温度。我们可以把美国地图上的温度数据叠加为灰度图像——较暗的区域代表较冷的温度,更明亮的区域代表较热的区域。这样的表现不仅令人难以置信,而且代表了两个重要的原因。首先,人类视觉系统没有被优化来测量灰度强度的微小变化。我们能更好地感知颜色的变化。其次,我们用不同的颜色代表不同的意思。用蓝色比用较温暖的红色表示较冷的温度更有意义。
using namespace cv;
Mat im_gray = imread("pluto.jpg", IMREAD_GRAYSCALE);
Mat im_color;
applyColorMap(im_gray, im_color, COLORMAP_JET);
数据结构与算法
并查集基本操作
class UnionFind
{
private:
vector<int> parent;
vector<int> rank;
int count;//连通分量个数
public:
UnionFind(int n);
~UnionFind();
//查找
int Find(int x){
//查找时进行路径压缩
return parent[x] == x ? x : parent[x] = Find(parent[x]);
}
//合并
bool Union(int x,int y){
int rx = Find(x);
int ry = Find(y);
if (rx == ry)
{
return false;
}
//如果深度相同,将rx 加入 ry
if (rank[rx] == rank[ry])
{
parent[rx] = ry;
rank[ry]++;
}else if (rank[rx] < rank[ry])
{//否则哪个深度小,就挂在对方集合中
parent[rx] = ry;
}else
{
parent[ry] = rx;
}
//连通数量减一
count --;
return true;
}
int getCount(void){
return count;
}
};
UnionFind::UnionFind(int n)
{
parent.resize(n);
rank.resize(n,1);
for (int i = 0; i < n; i++)
{
parent[i] = i;
}
count = n;
}
UnionFind::~UnionFind()
{
}
优先队列延迟删除技巧
优先队列是不支持移出非堆顶元素这一操作的,因此我们可以考虑使用「延迟删除」的技巧,即:
当我们需要移出优先队列中的某个元素时,我们只将这个删除操作「记录」下来,而不去真的删除这个元素。当这个元素出现在优先队列的堆顶时,我们再去将其移出对应的优先队列。
「延迟删除」使用到的辅助数据结构一般为哈希表delayed,其中的每个键值对 (num,freq),表示元素 num 还需要被删除freq 次。「优先队列 + 延迟删除」有非常多种设计方式,体现在「延迟删除」的时机选择。
我们保证在任意操作 完成之后(或者说任意操作开始之前),优先队列的堆顶元素都是不需要被「延迟删除」的。
可见滑动窗口中位数题解中的操作及使用介绍
//待更新