Python、C++、Opencv、数据结构与算法杂乱知识点

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 次。「优先队列 + 延迟删除」有非常多种设计方式,体现在「延迟删除」的时机选择。

我们保证在任意操作 完成之后(或者说任意操作开始之前),优先队列的堆顶元素都是不需要被「延迟删除」的。

可见滑动窗口中位数题解中的操作及使用介绍
//待更新

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值