Decorative image frame

Wlka

本以为是物是人非,不曾想是面目全非

Wlka

最长回文串

很久以前我就在leetcode上看到过这么一道题

1
2
3
4
5
6
7
8
9
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000

示例:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

输入: "cbbd"
输出: "bb"

这道题有很多种求解方法,讨论区也有不少人分享了他们的解法,其中就包括了暴力解法、动态规划求解、中心扩展法、Manacher(马拉车)算法,但是今晚我看到了另一种解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty() || s.length()==1)
return s;
//分别存放回文串的起点终点
int range[2]={0,0};
for(int i=0;i<s.length();++i)
{
i=findLongest(s,i,range);
}
//返回起点和终点间的字符串
return s.substr(range[0],range[1]-range[0]+1);
}

int findLongest(string s,int low,int range[])
{
int high=low;
//找到第一个不相同的位置
while(high<s.length()-1 && s[high+1]==s[low])
++high;
//把回文左右对称,保存中间位置
int ans=high;
//从中间向两边扩展
while(low>0 && high<s.length()-1 && s[low-1]==s[high+1])
{
--low;
++high;
}
//记录最大长度
if(high-low>range[1]-range[0])
{
range[0]=low;
range[1]=high;
}
return ans;
}
};

这个方法不仅不需要修改原有字符串或者额外申请O(n)的空间,而且时间复杂度为O(n^2),相比较于中心扩展法或马拉车算法,不需要考虑字符串长度为奇偶数的情况,也不需要修改字符串(在每个字符前添加一个’#’来确保字符串长度为偶数),Amazing!

从文件中恢复二叉树

假设有一个需求,将二叉树存入文件,并想办法从文件中读取出来,然后重构这棵二叉树

首先,要想办法把二叉树从文件数据中重构,那么一定要想办法找出合适的遍历算法将二叉树存入到文件

我们知道二叉树有四种遍历:前序、中序、后序、层次遍历,但是这里面只有一种遍历算法符合我们的要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**************************************************************
先假设我们有一个这样的二叉树
30
/ \
20 40
/ / \
10 35 50

它的中序遍历和后序遍历父节点都不在叶子节点前先输出,这样我么你无法确定父节点和它孩子的位置
但是前序遍历则没有这个问题,父节点总是在叶子节点前先输出,我们先构造父节点,然后确定孩子节点
但是在操作时,需要记录空节点,否则处理上会有问题
**************************************************************/

void readBST(struct node *&root, ifstream &fin)
{
int val;
fin >> val; //先读取根节点数据
readBSTHelper(INT_MIN, INT_MAX, val, root, fin);//传入父节点数据,递归调用函数来确定左右孩子
}
void readBSTHelper(int min,int max,int &insertVal,class node *&p,ifstream &fin)
{
if (insertVal > min && insertVal < max)
{
p = newNode(insertVal); //构造父节点
while(fin >> insertVal && insertVal=="#") fin>>insertVal;//继续读取数据,并跳过空节点
readBSTHelper(min, val, insertVal, p->left, fin);
readBSTHelper(val, max, insertVal, p->right, fin);
}
}

BFPRT算法

topK是一道经典面试题,就是在一堆数里找出第K大的数字

这道题有很多解法,这里介绍一下BFPRT算法

简介

BFPRT算法,又称为中位数的中位数算法,它的最坏时间复杂度为$O(n)$,它是由Blum、Floyd、Pratt、Rivest、Tarjan提出。该算法的思想是修改快速选择算法的主元选取方法,提高算法在最坏情况下的时间复杂度

分析

我们知道快速排序每次从序列中选取一个数作为基准,将序列中比基准大的数放在基准右边,比基准小的放在基准左边,一趟快速排序成为partition,我们无法确保每次划分得很均匀,所以最坏情况下不能保证时间复杂度为$O(n log n)$,而是$O(n^2)$。在BFPRT算法中,仅仅是改变了每趟快速排序的pivot值的选取,即每次将序列划分为5份,挑选中位数作为pivot,在每份里面进行插入排序

时间复杂度分析

$T(n)≤T(n/5)+T(7n/10)+c*n$

设$T(n)=t*n$,其中t可能是一个常数,也可能是关于n的函数,代入上式

$tn≤tn/5+t7n/10+cn $ -> $t≤10c$

c是常数,所以t也是常数,即$T(n)≤10c*n$,所以$T(n)=O(n)$


那么为什么是5呢?首先偶数排数,对于奇数来说中位数的选取更容易,如果选取7、9或是更大,插入排序时耗时增加,有些得不偿失,而选择3时,有$T(n)=T(n/3)+T(2n/3)+c*n$,其操作元素个数还是n

步骤

  1. 将n个元素划分为$n/5$组,每组5个
  2. 寻找着$n/5$个组中每组的中位数,可以用插入排序
  3. 对步骤2中的$n/5$个中位数重复步骤1、2,递归下去知道剩下一个数字
  4. 最终剩下的数字即为pivot,大于它的数放左边,小于小于它的放右边
  5. 判断pivot位置与k的大小,有选择的对左边或右边递归
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

//插入排序
void InsertSort(vector<int> &a, int l, int r)
{
for(int i=l+1; i<=r;i++)
{
if(a[i-1]>a[i])
{
int t=a[i];
int j=i;
while(j>l && a[j-1]>t)
{
a[j] = a[j-1];
j--;
}
a[j]=t;
}
}
}

//寻找中位数的中位数
int FindMid(vector<int> &a,int l,int r)
{
if(l==r) return l;
int i=0;
int n=0;
for(i=l; i<r-5; i+=5)
{
InsertSort(a,i,i+4);
n=i-l;
swap(a[l+n/5],a[i+2]);
}

//处理剩余元素
int num=r-i+1;
if(num>0)
{
InsertSort(a,i,i+num-1);
n=i-l;
swap(a[l+n/5],a[i+num/2]);
}
n/=5;
if(n==l) return l;
return FindMid(a,l,l+n);
}

//进行划分过程
int Partion(vector<int> &a,int l,int r,int p)
{
swap(a[p],a[l]);
int i=l;
int j=r;
int pivot=a[l];
while(i<j)
{
while(a[j]>=pivot && i<j)
j--;
a[i]=a[j];
while(a[i]<=pivot && i<j)
i++;
a[j]=a[i];
}
a[i]=pivot;
return i;
}

int BFPRT(vector<int> &a,int l,int r,int k)
{
int p=FindMid(a,l,r); //寻找中位数的中位数
int i=Partion(a,l,r,p);

int m=i-l+1;
if(m==k) return a[i];
if(m>k) return BFPRT(a,l,i-1,k);
return BFPRT(a,i+1,r,k-m);
}

int main()
{
int n,k; //数据数量和K值
cin>>n;
vector<int> a(n,0);
for(int i=0;i<n;i++) cin>>a[i];
cin>>k;
for(auto i:a) cout<<i<<" ";
cout<<"\n第"<<k<<"大数字: "<<BFPRT(a,0,n-1,k)<<endl;
return 0;
}

vim使用命令

vim基本操作

编辑模式

命令 用法
i/I 从光标所在位置插入/在目前所在行第一个非空格符处插入
a/A 从光标所在下一个字符处插入/在目前所在行最后一个字符处插入
o/O 从光标所在下一行输入新行/从光标所在上一行输入新行
r/R 只取代光标所在字符一次/一直取代光标所在文字直到按下Esc

指令行模式

命令 用法
:w 保存
:w! 强制写入只读文件,但是还是跟权限有关
:q 退出vim
:q! 退出vim但是不修改文件
:wq 保存退出
:wq! 强制保存退出
ZZ 文件被修改则保存后离开,否则直接离开
:w filename 将数据保存到另一个文件,相当于另存为
:r filename 将filename的数据加到编辑数据的光标所在行后面
:n1,n2 w filename 将n1到n2行的内容保存到filename文件中
:!command 暂时离开vim到指令行模式执行command的显示结果

vim环境变更

命令 用法
:set nu 在每行前显示行号
:set nonu 取消显示行号

一般模式

  • 移动光标
命令 用法
hjkl 光标左下上右移动一个字符,在前面搭配数字可以实现快速移动,方向键效果一样
Ctrl+f 效果同Page Down
Ctrl+b 效果同Page Up
Ctrl+d 向下移动半页
Ctrl+u 向上移动半页
+/- 光标移动到非空格符下/上一行
n+space 光标向右移动这行的n个字符
n+Enter 光标向下移动n行
0 效果同Home
$ 效果同End
H/L 光标移动到屏幕最上/下方那行的第一个字符
M 光标移动到屏幕中央那行第一个字符
G 移动到这个文件最后一行
nG 移动到这个文件第n行
gg 移动到文件第一行
  • 搜索替换
命令 用法
/word 向光标之下寻找一个名为word的字符串
?word 向光标之上寻找一个名为word的字符串
n 重复一次前一个搜索动作
N 反向重复一次前一个搜索动作
:n1,n2s/word1/word2/g[c] 在n1到n2行之间用word2取代word1,最后一个c是可选项,表示取代前提示用户确认
:1,$s/word1/word2/g 从第一行到最后一行将word1替换为word2
:%s/word1/word2/g 效果同上
  • 删除/复制/粘贴
命令 用法
x/X 向后/前删除一个字符,效果同delete和backspace
nx/nX 连续向后/前删除n个字符
dd 删除光标所在的整行
ndd 删除光标所在的向下n行
d1G 删除光标所在到第一行的所有数据
dG 删除光标所在到最后一行的所有数据
d$ 删除光标所在行到该行最后一个字符
d0 删除光标所在行到该行最前面一个字符
yy 复制光标所在行
nyy 复制光标所在的向下n行
y1G 复制光标所在到第一行的所有数据
yG 复制光标所在到最后一行的所有数据
y$ 复制光标所在行到该行行尾的所有数据
y0 复制光标所在行到该行行首的所有数据
p/P 将复制的数据粘贴到光标下/上一行
J 将光标所在行与下一行数据结合成一行
c 重复删除多个数据,例如10ck为向上删除10行
u 复原前一个动作,类似撤销
Ctrl+r 重做上一个动作,类似取消撤销
. 重复上一个动作

one more thing

  • Ctrl+v可以进入块选择模式
  • :n1,n2s/^/注释符/g可以快速在多行行首添加注释
  • :n1,n2s/^注释符//g可以快速取消多行注释

打开多文件

  • vim file1 file2 file3
  • :n #跳到下一个文件
  • :n filename #跳到指定文件
  • :e# #回到刚才编辑的文件

打开多窗口

  • vim -p file1 file2 file3 #标签页形式打开多窗口
  • vim -o[-O] file1 file2 file3 #水平/垂直打开多窗口
  • vim -d[diff] file1 file2 file3 #垂直打开多窗口,并且进行比较

关闭窗口

  • :q/:close #关闭当前窗口
  • :only #保留当前窗口,关闭其他所有窗口
  • :qall/qa #退出所有窗口
  • :wall #保存所有窗口

窗口切换

  • :Ctrl+w+j/k上下切换,也可以Ctrl+w+w一次切换窗口

排序算法

几个排序算法的实现与复杂度分析

冒泡排序

每次从左到右两两比较,将大的交换到后面,每次确保前M个元素最大值移到最右边

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <vector>
#include <time.h>

using namespace std;

void bubble_sort(vector<int> &vec)
{
for(int i=0;i<vec.size()-1;++i)
{
for(int j=0;j<vec.size()-i-1;++j)
{
if(vec[j]>vec[j+1]) swap(vec[j],vec[j+1]);
}
}
}

int main()
{
vector<int> vec;
for(int i=0;i<10;++i)
{
vec.push_back(rand()%20);
}
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
bubble_sort(vec);
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
return 0;
}

复杂度:重复执行冒泡n次,每次进行n次比较,时间复杂度为$O(n^2)$,空间复杂度为$O(n)$

插入排序

从左到右,把选出的数和前面的数比较,找到最适合它的位置放入,使得前面部分有序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <vector>
#include <time.h>

using namespace std;

void insertion_sort(vector<int> &vec)
{
for(int i=1;i<vec.size();++i)
{
for(int j=i;j>0;--j)
{
if(vec[j]<vec[j-1]) swap(vec[j],vec[j-1]);
}
}
}

int main()
{
vector<int> vec;
for(int i=0;i<10;++i)
{
vec.push_back(rand()%20);
}
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
insertion_sort(vec);
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
return 0;
}

复杂度:要选择n次,插入时最坏情况下要比较n次,时间复杂度为$O(n^2)$,空间复杂度为$O(n)$

选择排序

每次都从乱序数组中找到最值,放在数组的一端,最终使得数组有序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <vector>
#include <time.h>

using namespace std;

void selection_sort(vector<int> &vec)
{
for(int i=0;i<vec.size();++i)
{
int min=i;
for(int j=i+1;j<vec.size();++j)
{
if(vec[j]<vec[min]) min=j;
}
swap(vec[min],vec[i]);
}
}

int main()
{
vector<int> vec;
for(int i=0;i<10;++i)
{
vec.push_back(rand()%20);
}
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
selection_sort(vec);
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
return 0;
}

复杂度:每次都要找最值,最坏情况下要比较n次,过程执行了n次,时间复杂度为$O(n^2)$,空间复杂度为$O(n)$

希尔排序

可以看作是插入排序优化版,以发明者的名字命名,另一个名字是”递减增量排序算法”;插入排序对几乎已排好序数据操作时,可以达到线性排序的效率,但由于每次只能移动一位,所以相对低效,希尔排序将数据分为几个子数组进行排序,最后一步才是普通的插入排序,此时数据已经几乎有序,所以最后一步较快

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <vector>
#include <time.h>

using namespace std;

void shell_sort(vector<int> &vec)
{
int gap=1; //把步长先设为1
while(gap<vec.size()/3) gap=gap*3+1; //把数组分为3部分进行排序
while(gap>=1)
{
for(int i=gap;i<vec.size();++i)
{

for(int j=i;j>=gap&&vec[j]<vec[j-gap];j-=gap)
{
swap(vec[j],vec[j-gap]);
}
}
gap=gap/3;
}
}

int main()
{
vector<int> vec;
for(int i=0;i<10;++i)
{
vec.push_back(rand()%20);
}
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
shell_sort(vec);
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
return 0;
}

复杂度:希尔排序的时间复杂度受步长的影响,设N为数组长度,K为当前增量,当$K=2^x$时,时间复杂度为$O(n^2)$;当$K=3x+1$时,时间复杂度为$O(n^{3/2})$,Mark Allen Weiss指出,最好的增量序列是Sedgewick提出的 (1, 5, 19, 41, 109,…),该序列的项来自 $9 4^i - 9 2^i + 1$ 和 $4^i - 3 * 2^i + 1$ 这两个算式

归并排序

分治法,把一个数组打散成小数组,再把小数组拼凑后进行排序,知道最后数组有序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <vector>
#include <time.h>

using namespace std;

void merge_vec(vector<int> &vec,vector<int>& tmpVec,int start,int middle,int end)
{
for(int s=start;s<=end;++s) tmpVec[s]=vec[s];
int left=start;
int right=middle+1;
for(int k=start;k<=end;++k)
{
if(left>middle) vec[k]=tmpVec[right++];
else if(right>end) vec[k]=tmpVec[left++];
else if(tmpVec[right]<tmpVec[left]) vec[k]=tmpVec[right++];
else vec[k]=tmpVec[left++];
}
}

void merge_sort(vector<int> &vec,vector<int>& tmpVec,int start,int end)
{
if(start>=end) return;
int middle=start+(end-start)/2;
merge_sort(vec,tmpVec,start,middle);
merge_sort(vec,tmpVec,middle+1,end);

merge_vec(vec,tmpVec,start,middle,end);
}

void merge_sort(vector<int> &vec)
{
vector<int> tmpVec(vec.size(),0);
merge_sort(vec,tmpVec,0,vec.size()-1);
}

int main()
{
vector<int> vec;
for(int i=0;i<10;++i)
{
vec.push_back(rand()%20);
}
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
merge_sort(vec);
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
return 0;
}

复杂度:在合并数组操作中只有一次循环,复杂度为$O(n)$,分解数组是对半切割,复杂度为$O(log n)$,所以总体时间复杂度为$O(nlog n)$,由于使用到了额外的空间来存放临时数组,所以空间复杂度为$O(n)$

快速排序

分治法,选择一个基准数,把比它小的数放在它左边,比它大的数放在它右边,然后对左右两部分进行同样的操作,直到整个数组有序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <vector>
#include <time.h>

using namespace std;

int partition(vector<int> &vec,int low,int high)
{
int pivot = vec[low];
int i=low;
while (low<high)
{
//要先移动右边
while (vec[high] > pivot && low < high) --high;
while (vec[low] <= pivot && low < high) ++low;
if (low < high) swap(vec[low],vec[high]);
}
swap(vec[i],vec[low]);
return low;
}

void quick_sort(vector<int> &vec,int low,int high)
{
if(low>=high) return;
int middle=partition(vec,low,high);
quick_sort(vec,low,middle-1);
quick_sort(vec,middle+1,high);
}

void quick_sort(vector<int> &vec)
{
if(vec.size()<2) return;
quick_sort(vec,0,vec.size()-1);
}

int main()
{
vector<int> vec;
for(int i=0;i<10;++i)
{
vec.push_back(rand()%100);
}
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
quick_sort(vec);
for(auto i:vec) cout<<i<<"\t";
cout<<endl;
return 0;
}

复杂度:快速排序时间复杂度为$O(nlog n)$,空间复杂度为$O(1)$,但这是建立在每次切分都能把数组分为两半的前提下,极端情况下,时间复杂度会退化成$O(n^2)$,当然这概率也比较低

堆排序

堆其实是一颗完全二叉树,分为大顶堆和小顶堆,实际上可以用一个一维数组表示,下标公式如下:

  • $i$节点的父节点$parent(i)=floor((i-1)/2)$
  • $i$节点的左子节点$left(i)=2i+1$
  • $i$节点的右子节点$right(i)=2i+2$

步骤:

  1. 构造大顶堆(Build Max Heap):首先将当前元素放入最大堆下一个位置,然后将此元素依次和它的父节点比较,如果大于父节点就和父节点交换,直到根节点,重复执行到最后一个元素
  2. 大顶堆调整(Max Heapify):调整大顶堆,将根节点移除后重新整理堆,即让根节点和最大一个节点交换,然后把堆看做$n-1$长度,将当前根节点逐步移动到其应该在的位置
  3. 堆排序(HeapSort):重复执行2,知道所有根节点都被移除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void heap_sort(vector<int> &vec)
{
int size = vec.size();
buildHeap(vec,length); //构建堆
for ( int i = size - 1; i > 0; i-- )
{
swap(vec[0],vec[i]); //将堆顶元素与末位元素调换
size--; //数组长度-1 隐藏堆尾元素
sink(vec,0,size); //将堆顶元素下沉 目的是将最大的元素浮到堆顶来
}
}
void buildHeap(vectror<int> &vec,int size)
{
for (int i = size / 2; i >= 0; i--)
sink(vec,i,size);
}

/**
* 下沉调整
* @param arr 数组
* @param index 调整位置
* @param length 数组范围
*/
void sink(vector<int> &vec,int index,int size)
{
int leftChild = 2 * index + 1; //左子节点下标
int rightChild = 2 * index + 2; //右子节点下标
int present = index; //要调整的节点下标

//下沉左边
if (leftChild < size && vec[leftChild] > vec[present])
present = leftChild;

//下沉右边
if (rightChild < length && vec[rightChild] > vec[present])
present = rightChild;

//如果下标不相等 证明调换过了
if (present != index)
{
//交换值
swap(vec[index],vec[present]);

//继续下沉
sink(vec,present,size);
}
}

复杂度:时间复杂度为$O(nlog n)$

计数排序

找到数组里数据的最大值max,并创建一个最大下标为max的空数组arr,遍历数组,将数据的出现次数填入arr对应的位置,遍历arr,依次取出即可完成排序,但是局限性很大,只适用于正整数且范围相差不大的数组排序使用,但速度相当可观

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void count_sort(vector<int> &vec)
{
//找出数组中的最大值
int max = vec[0];
for (int i = 1; i < vec.size(); i++)
{
if (vec[i] > max) {
max = vec[i];
}
}
//初始化计数数组
vector<int> countArr(max+1,0);

//计数
for (int i = 0; i < vec.size(); i++)
{
countArr[vec[i]]++;
arr[i] = 0;
}

//排序
int index = 0;
for (int i = 0; i < countArr.size(); i++)
{
if (countArr[i] > 0) {
vec[index++] = i;
}
}
}

复杂度:时间复杂度为$O(n+m)$,m为数据量

稳定排序

保持排序前后相同数据的顺序不变,对计数排序进行变形,countArr存放的是数据出现次数,那么我们可以将countArr里的每个元素顺序求和,取数据的时候则要把排名减1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void stable_sort(vector<int> &vec)
{
//找出数组中的最大值
int max = vec[0];
for (int i = 1; i < vec.size(); ++i) {
if (vec[i] > max) {
max = vec[i];
}
}

//初始化计数数组
vector<int> countArr(max + 1,0);

//计数
for (int i = 0; i < ve.size(); ++i)
countArr[arr[i]]++;

//顺序累加
for (int i = 1; i < max + 1; ++i)
countArr[i] = countArr[i-1] + countArr[i];

//排序后的数组
vector<int> sortedArr(vec.size(),0);

//排序
for (int i = vec.size() - 1; i >= 0; --i)
{
sortedArr[countArr[vec[i]]-1] = arr[i];
countArr[arr[i]]--;
}

//将排序后的数据拷贝到原数组
for (int i = 0; i < vec.size(); ++i)
vec[i] = sortedArr[i];
}

桶排序

可以看作是基数排序的升级版,把要排序的数据分到多个有序的桶里,每个桶里单独进行排序,然后依次取出即可完成排序,这里有几个问题需要考虑到:桶的表示、桶数量的确定、桶内排序的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void sort(vector<int> &vec){
//最大最小值
int max = vec[0];
int min = vec[0];
int size = vec.size();

for(int i=1; i<size; i++)
{
if(vec[i] > max)
max = vec[i];
else if(vec[i] < min)
min = vec[i];
}

//最大值和最小值的差
int diff = max - min;

//桶列表
vector<vector<int>> bucketVec(size,vector<int>); //桶的数量我设置成数组的长度

//每个桶的存数区间
float section = (float) diff / (float) (length - 1);

//数据入桶
for(int i = 0; i < size; i++){
//当前数除以区间得出存放桶的位置 减1后得出桶的下标
int num = (int) (vec[i] / section) - 1;
if(num < 0) num = 0;
bucketVec[num].push_back(vec[i]);
}

//桶内排序
for(int i = 0; i < bucketVec.size(); i++)
sort(bucketVec.begin(),bucketVec.end());

//写入原数组
int index = 0;
for(auto b:bucketVec)
for(auto value:b)
vec[index++]=value;
}

复杂度:最好情况下,即桶内没有进行排序,时间复杂度为$O(n)$,但是在桶与桶之间数据分布极不均匀的情况下,时间复杂度退化为$O(nlog n)$,解决办法是每次桶排序前判断一下数据量,如果桶内数据量过大,应该在桶内回调自身再进行一次桶排序

基数排序

非比较型整数排序算法,将数据按位数切割成不同的数字,然后按每个位数分别比较,不管你的数位有多大,按位数排序,最多也就0-9十个桶,先按权重小的位置排序,然后按权重大的位置排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void sort(vector<int> &vec)
{
int size = arr.size();

//最大值
int max = vec[0];
for(int i = 0;i < size;i++){
if(vec[i] > max){
max = vec[i];
}
}
//当前排序位置
int location = 1;

//桶列表
vector<vector<int>> bucketVec(10,vector<int>); //桶的数量我设置成数组的长度

while(true)
{
//判断是否排完
int dd = (int)Math.pow(10,(location - 1));
if(max < dd){
break;
}

//数据入桶
for(int i = 0; i < size; i++)
{
//计算余数 放入相应的桶
int number = ((vec[i] / dd) % 10);
bucketVec[number].push_back(vec[i]);
}

//写回数组
int nn = 0;
for (int i=0;i<10;i++){
int s = bucketVec[i].size();
for(int ii = 0;ii < s;ii ++){
vec[nn++] = bucketVec[i][ii];
}
bucketVec[i].clear();
}
location++;
}
}

网易内推笔试总结

  • 写在前面

今天下午做了一下网易的内推笔试,这段时间一直在刷题,没注重基础知识的补充,所以在问答题上吃了大亏(问答题有两道,一道是关于智能指针的,另一道是inline函数的),下面就来总结一下


smart pointer

在c++标准里,智能指针有4种,auto_ptr、shared_ptr、weak_ptr、unique_ptr,其中后面三个是c++11新加进来的,第一个是c++98的,在新标准中已经被抛弃

auto_ptr(所有权模式)
1
2
3
4
5
auto_ptr<string> p1(new string("test"));
auto_ptr<string> p2;
p2=p1; //此时并不报错
//p2剥夺了p1的所有权,当程序运行时如果访问到p1将会报错
//这是auto_ptr的缺点:存在潜在的内存崩溃问题
unique_ptr(替换auto_ptr,所有权模式,独占式拥有或严格拥有)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//保证同一时间内只有一个智能指针可以指向该对象,对于避免资源泄露特别有用
unique_ptr<string> p3(new string("test"));
unique_ptr<string> p4;
p4=p3; //报错,编译器认为p3=p4非法

//此外,unique_ptr还有一个更聪明的地方,当程序试图将一个unique_ptr赋值给另一个时,如果源unique_ptr是临时右值,编译器允许会这么做,但是如果源unique_ptr将存在一段时间,编译器将禁止这一行为
unique_ptr<string> p5(new string("test"));
unique_ptr<string> p6;
p6=p5; //非法,因为这样会留下悬挂的p5,可能有潜在危害
//如果非要这么做的话,需要用安全的做法,同时也能给它赋新值
p6=move(p5); //转移所有权
p5=new string("new string"); //赋新值

unique_ptr<string> p7=unique_ptr<string>(new unique_ptr<string>("test")); //允许
shared_ptr(共享式拥有,多个智能指针指向同一对象,当最后一个引用被销毁时资源释放)
1
2
3
4
5
6
7
8
9
//通过计数机制表明资源b被几个指针共享,可以通过use_count()查看资源所有者个数,可通过new进行构造,也可通过auto_ptr、unique_ptr、weak_ptr来构造,当调用release()时,当前指针释放所有权,计数减1,当计数为0时资源释放
/*
成员函数如下:
use_count 返回引用计数个数
unique 返回是否是独占所所有权(use_count为1)
swap 交换两个shared_ptr所拥有的对象
reset 放弃内部对象的所有权或拥有对象的变更,会引起原有对象引用计数的减少
get 返回内部对象指针,由于已经重载了()方法,因此和直接使用对象是一样的
*/
weak_ptr(不控制对象生命周期的智能指针)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
指向一个shared_ptr管理的对象,进行该对象的内存管理是那个强引用的shared_ptr
weak_ptr只是提供了对管理对象的一个访问手段
设计目的是为配合shared_ptr而引入的一种智能指针来协助shared_ptr进行工作
它可以从一个shared_ptr或l另一个weak_ptr对象构造,其构造和析构不会引起计数的变化
用来解决shared_ptr的死锁问题
*/
class B;
class A
{
public:
shared_ptr<B> pb_;
~A();
};

class B
{
public:
shared_ptr<A> pa_;
~B();
};

void fun()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());

//pa和pb相互引用
pb->pa_=pa;
pa->pb_=pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}

int main()
{
fun();
return 0;
}

/*
当跳出fun函数时,智能指针pa和pb析构时两个资源引用计数会减1,但二者引用计数还是为1,导致跳出函数时资源没有b被释放(即析构函数没有被调用),这时把其中一个shared_ptr改为weak_ptr即可

另外,weak_ptr不能直接访问对象的方法,需要先转化为shared_ptr才能进行操作
*/

inline

  • 又称内联函数,用来建议编译器对一些特殊的函数进行内联扩展,节省每次调用函数的开支,但是需要在占用空间和程序效率上进行权衡,对过多的复杂的函数进行内联会带来很大的存储资源开支,对于递归函数的内联扩展则可能会引起部分编译器的无穷编译;

  • 与宏相比较:宏调用不检查类型,而且进行的是文本替换,需要计算操作顺序,内联代码更方便调试

  • 不足之处是对于基于C的编译系统,内联函数的使用在代码量增加的同时也可能大大增加编译时间

iData习题课

Python命令注册

函数有三个参数: modelName(模块名或文件名)、methodName(无参函数名称)、message(命令提示信息)
注意: 注册命令后需要设置文件下次自动加载,重启iData后才有效果

1
2
3
4
5
6
7
8
#filename: myDemo.py
from AddCommand import AddCommand

def pyMyPrint():
print("hello world")

AddCommand('myDemo','pyMyPrint','Python打印')
# 模块名 函数名 函数说明

Python文件加密

iData提供了加密.py文件的设置。apploadpython之后,会看到”加密”选项,点击之后能将.py文件加密成不可解密的.pyc文件,只能被加载,不能被修改

高程点的展绘和提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import PyiData as pid

def pyTestDrawDAT():
if not pid.isDocumentActived():
print("文档未打开")
pid.iDataPrintf("文档未打开")
return

db=pid.getCurBase() #获取数据库
geoCode=db.getGeoCodeIndex('72010010') #获取高程点编码对象
if not geoCode.isValid():
print("当前数据库不包含高程点编码")
return

entityList=pid.iDataEntityList()

datFile=pid.getSourcePath()+"/demo/STUDY.DAT"

for line in open(datFile):
line=line.strip('\n') #按行读取文件
temp=line.split(',') #拆分字符串

#当读取没问题且高程不为0,将数据添加进列表
if len(temp)==5 and temp[4]!=0:
pos=pid.Point3D(float(temp[3]),float(temp[2]),float(temp[4]))
point=pid.iDataPoint(geoCode,pos)
entityList.append(point) #创建点实体并加入列表

if len(entityList)>0:
pid.iDataAppendEntity(entityList) #提交
pid.CommitEntity(entityList,pid.iData.kAppended)
#pid.iDataExcuteCommand('zoom;e',500) #延迟0.5秒执行缩放全图命令
else:
print("没有添加任意高程点")

def pyTestWriteDAT():
if not pid.isDocumentActived():
print("文档未打开")
pid.iDataPrintf("文档未打开")
return

#选择地貌点图层下的所有点
e,entityList=pid.iDataSSGetX("TERPT")
if len(entityList)==0:
print("地貌点图层没有实体")
return

#打开保存文件对话框
filename=pid.BasicUI_getSaveFileName("选择要保存的文件","C:\\","DAT文件(*.dat)")
if len(filename)==0:
print("放弃输出DAT文件")
return

file=open(filename,"w") #以写入方式打开文件
num=0
for entity in entityList:
point=pid.iDataEntity.convertToPoint(entity) #将抽象实体变为点实体
if point.getCode()!="72010010": #过滤编码不是高程点的点
continue

pos=point.getNode() #取得点坐标
num+=1
dat_line="%d,,%.3f,%.3f,%.3f\n"%(num,pos.y(),pos.x(),pos.z()) #设置写入格式
file.write(dat_line) #写入文件
file.close() #关闭文件

# pyTestDrawDAT()
pyTestWriteDAT()

统计植被面积以及天然草地在植被中的面积占比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import math
import PyiData as pid

def pyTestStaticArea():
if not pid.isDocumentActived():
print("文档未打开")
pid.iDataPrintf("文档未打开")
return

# 手动打开文档
# mdbFileName=pid.getSourcePath()+"/demo/study.mdb"
# isOk=pid.OpenDoc(mdbFileName)
# if not isOk:
# print("无法打开MDB文件")
# return

#选择植被图层上的全部实体
e,entityList=pid.iDataSSGetX("VEGNT")
if len(entityList)==0:
print("植被面图层没有实体")
return

area_total=0.0
area=0.0
for entity in entityList:
area_total+=entity.getArea() #统计植被面积
if entity.getCode()=="81060230": #统计天然植被面积
area+=entity.getArea()
entity.setHighLight(True)

if math.fabs(area_total)>0:
per=100*area/area_total
print("当前图面植被面积%3.2f m^2, 其中天然草地面积%3.2f m^2, 占%.1f%%"%(area_total,area,per))
else:
print("无法统计面积")

输出Excel报表

实例代码为每一个房屋面输出一个Excel信息表,表中有房屋的各种属性以及渲染图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import PyiData as pid
import xlsxwrite

def pyTestWriteXLSX():
if not pid.isDocumentActived():
print("文档未打开")
pid.iDataPrintf("文档未打开")
return

# 选择居民地面图层没有的实体
e, entityList = pid.iDataSSGetX("RESNT")
if len(entityList) == 0:
print("居民地面图层没有实体")
return

# 打开保存文件对话框
fileDir = pid.BasicUI_getExistingDirectory("选择要保存EXcel的文件夹", "C:\\")
if len(fileDir) == 0:
print("放弃输出文件")
return

if not fileDir.endswith('/'):
fileDir += '/'

pid.iDataCreateProgress("输出房屋信息") # 创建进度条
current = 0
for entity in entityList:
current += 1
pid.iDataSetProgressValue(current, entityList.size()) # 修改进度条数据

if current > 10: # 测试过程减少输出数量
print("可输出Excel数量超过10,测试超过10即停止")
break

oid = str(entity.getOID()) # 用OID做名称
xlsxName = fileDir + oid + ".xlsx" # Excel文件名

workBook = xlsxwriter.Workbook(xlsxName) # 创建工作簿
workSheet = workBook.add_worksheet() # 添加一个Sheet

# 设置列宽
workSheet.set_column('A:A', 20)
workSheet.set_column('B:B', 40)

# 设置以及合并表头
merge_format = workBook.add_format({
'font_size': 16,
'bold': 1,
# 'bolder': 1,
'align': 'center',
'valign': 'vcenter',
'fg_color': 'yellow'
})
workSheet.merge_range('A1:B1', 'XXX房屋信息表', merge_format)

# 设置单元格格式
bold = workBook.add_format({'bold': True, 'align': 'center'})

# 写入数据
workSheet.write('A2', '房屋层数: ', bold)
workSheet.write_number('B2', entity.getXData('FWSC').toInt())
workSheet.write('A3', '地下房屋层数: ', bold)
workSheet.write_number('B3', entity.getXData('DXFWSC').toInt())
workSheet.write('A4', '房屋类型: ', bold)
workSheet.write('B4', entity.getXData('FWLX').toString())
workSheet.write('A5', '名称: ', bold)
workSheet.write('B5', entity.getXData('MC').toString())
workSheet.write('A6', '类别名称: ', bold)
workSheet.write('B6', entity.getName())

# 插入图片
workSheet.merge_range('A8:B22', '房屋图片')
picName = fileDir + oid + '.png'
pid.saveEntitysToImage(picName, 500, 300, entity)
workSheet.insert_image('A8', picName, {'x_scale': 0.8, 'y_scale': 0.8}) # 缩放为80%

workBook.close() # 关闭工作簿

pid.iDataCloseProgress() # 关闭进度条

数据库图层编码字段

数据库DbBase

表示一个打开数据库对象,可获取数据中的信息

  • dbName: 获取数据库路径
  • Type: 数据库类型
  • geoCodeList: 获取实体编码列表
  • layerList: 获取全部图层列表
  • getLayer: 获取指定图层
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    dbBase=pid.getCurBase() #获取数据库对象
    print('数据库路径: ',dbBase.dbName())
    #kDBDefault\kSqlite\kGisPdb\kShape\kSDE\KCAD\KDGN\kGisEdb
    print('是不是MDB数据库?',dbBase.Type()==pid.iData.kGisPdb)

    layers=dbBase.layerList() #获取所有图层
    print('数据库中有',len(layers),'个图层,分别是: ')
    for i in layers: #打印图层名和别名
    print(i.getName(),i.getAliasName())

    geoCodes=dbBase.geoCodeList() #获取所有编码
    print('数据库中有',len(geoCodes),'个编码')

    e,layer=dbBase.getLayer('ROANT') #获取指定图层
    if e==pid.iData.eOk and layer.isValid():
    print('本数据库中存在ROANT图层')
    else:
    print('LCA图层不存在')

图层iDataLayer

  • getName: 图层名
  • getAliasName: 图层别名
  • getLayerType: 图层类型
  • getGeoCodeList: 图层上的编码列表
  • items: 图层包含的所有实体
  • xDataFields: 所有扩展属性字段
  • field: 获取指定属性字段
  • itemsBoundingRect: 图层外包矩形
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    dbBase=pid.getCurBase() #获取数据库对象
    e,layer=dbBase.getLayer('TERLK') #获取指定图层
    if e==pid.iData.eOk and layer.isValid():
    print('图层名',layer.getName(),'别名',layer.getAliasName())
    entitys=layer.items() #获取图层上的所有实体,同iDataSSGetX
    print('图层上有',len(entitys),'个实体')
    del entitys
    geoCodes=layer.getGeoCodeList() #获取本图层额编码列表
    print('TERLK图层中有',len(geoCodes),'个编码,分别是: ')
    for c in geoCodes:
    print(c.getCode(),c.getUserName()) #编码以及用户定义的名称
    else:
    print('TERLK图层不存在')

编码

1
2
3
4
5
6
7
8
9
10
dbBase=pid.getCurBase() #获取数据库对象
geoCode=dbBase.getGeoCodeIndex('76010221')
if geoCode.isValid():
print(geoCode.getCode(),':',geoCode.getUserName())
color=geoCode.getColorIndex()
print('颜色索引',color)
color=pid.iData_GetColor(color) #通过索引拿到颜色RGB
print('颜色',color)
else:
print('当前数据库中不包含(76010221)的编码')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#这里有个自定义的函数v2i需要注意一下
def v2i(d):
v=pid.Variant()
v.setValue(d)
return v

db=pid.getCurBase() #获取数据库
print('Current DataBase is: ',db.dbName())
#获取建成房屋(31030130)的编码对象
geoCode=db.getGeoCodeIndex('31030130')
if not geoCode.isValid():
print('当前数据库不包含建成房屋(31030130)的编码')
e,point=pid.iDataGetPoint('在图面上点选一个点')
if e!=pid.iData.eOk:
print('用户放弃选择')

x0=point.x()
y0=point.y()

arrNodes=pid.Point3DListList()
#绘制面,需要闭合的点,最好是顺时针
posList=pid.Point3DList()
posList.append(pid.Point3D(x0,y0,0))
posList.append(pid.Point3D(x0,y0+30,0))
posList.append(pid.Point3D(x0+30,y0+30,0))
posList.append(pid.Point3D(x0+30,y0,0))
posList.append(pid.Point3D(x0,y0,0))
arrNodes.append(posList)

#创建实体
entity=pid.iDataPolygon(geoCode,arrNodes,pid.DoubleListList())

#设置各种属性(必须是实体编码中存在的字段)
#print(pid.version<'1.3.26548')
'''
entity.setXData('FWCS',5)
entity.setXData('DXFWCS',1)
entity.setXData('FWLX','一般房屋')
entity.setXData('GD',18.6)
entity.setXData('JGLX','砼')
entity.setXData('YT','住宅')
'''
#版本1.3.26548以下只能用下面的写法,高版本直接用上面的写法
#低版本需要做个数据的类型转换
entity.setXData('FWCS',v2i(5))
entity.setXData('DXFWCS',v2i(1))
entity.setXData('FWLX',v2i('一般房屋'))
entity.setXData('GD',v2i(18.6))
entity.setXData('JGLX',v2i('砼'))
entity.setXData('YT',v2i('住宅'))
entity.setXData('MC',v2i('测试房屋'))

pid.iDataAppendEntity(entity) #将实体提交到图面
pid.CommitEntity(entity,pid.iData.kAppended) #提交到数据库
pid.iDataRegen(None,True) #刷新图面

接口

  • 文档
    iData文档相关接口常用的方法如下
    OpenDoc: 打开文件
    CloseDoc: 关闭文件
    getCurBase: 获取当前文件所打开的数据库对象
    getCurDocPath: 当前文档的路径
    getSourcePath: 获取当前运行程序的运行环境路径
    isDocumentActived: 判断当前视图中是否有文档打开

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    isOk=pid.isDocumentActived()  #获取文档打开状态
    docPath='D:\\A\\Program Pratice\\iData\\idata-standard-1.3\\demo\\study.mdb'
    if not isOk:
    isOk=pid.OpenDoc(docPath)
    print('打开文档',docPath)
    else:
    db=pid.getCurBase() #获取数据库对象
    print('当前打开数据库: ',db.dbName()) #获取数据库的名称
    docPath=pid.getCurDocPath() #获取当前打开的文档路径
    print('已经打开文档',docPath)
    isOk=pid.CloseDoc(docPath) #关闭文档
    print('关闭文档',docPath)

    print(pid.getSourcePath()) #获取iData程序运行路径
  • 对话框
    iData接口,包含了大部分常用操作
    getOpenFileName: 打开文件对话框
    getOpenFileNames: 打开文件对话框(多选)
    getExistingDirectory: 打开目录对话框
    getSaveFileName: 保存文件对话框
    GetCustomColorDlg: 选择颜色对话框

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #打开文件对话框
    fileName=pid.getOpenFileName('选择单个文件','D:\\','数据库(*.mdb)')
    print('选择了文件',fileName)

    #保存文件对话框
    fileName=pid.getSaveFileName('保存文件','D:\\')
    print('保存了文件',fileName)

    #打开多选文件对话框
    fileNameList=pid.getOpenFileName('选择多个文件','D:\\','数据库(*.mdb)')
    print('选择了文件',len(fileNameList),'个: ')
    for name in fileNameList:
    print(name)

    #选择目录对话框
    dirName=pid.getExistingDirectory('Python选择目录','D:\\')
    print('选择了文件夹: ',dirName)

    #选择颜色对话框
    ci=pid.getCustomColorDlg(0)
    print(ci) #用户选择了数据源中的颜色
    c=pid.iData_GetColor(ci) #通过索引拿到颜色RGB
    print(c)
  • 交互
    iDataPrintf: 打印
    iDataInitGet: 初始化关键字
    iDataGetString: 获取字符串
    iDataGetInt: 获取整数数值
    iDataGetReal: 获取浮点数值
    iDataGetPoint: 选择一个点
    iDataGetCorner: 拉框选择点
    iDataGetOrient: 获取线段和X轴的夹角

  • 选择
    iData接口包含丰富强大的选择功能,可根据不同的需求组合选择出所需要的实体,常用的选择接口如下:
    iDataEntSel: 单击鼠标选择实体
    iDataSSGetX: 选择给定的图层上的实体
    iDataSSGet: 根据给定的点、矩形、多边形等,在给定图层上选择实体
    iDataGetSelectedEntitys: 获取视图中被选中的实体
    ClearSelection: 取消对实体的选择状态
    iDataGetItemsBoundingRect: 获取视图中所有实体的最大外包矩形

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    #用鼠标在屏幕上点击选择单个实体
    e,entity,point=pid.iDataEntSel('鼠标屏幕点选实体: ')
    if e==pid.iData.eOk:
    print('选择了一个实体')
    print(entity)
    print('点击位置: ',point)

    #用鼠标以任意方式选择实体
    e,entityList=pid.iDataSSGet('选择实体: ')
    if e==pid.iData.eOk:
    print('选了',len(entityList),'个实体')

    e,entityList=pid.iDataSSGetX() #选择图面所有实体
    if e==pid.iData.eOk:
    pid.iDataDeleteEntity(entityList) #从图面删除
    pid.CommitEntity(entityList,pid.iData.kErased) #从数据库中删除
    pid.iDataRegen(None,True)
    del entityList #清空列表

    e,entityList=pid.iDataSSGetX(['IDATA','ROANT']) #选择图面所有实体
    if e==pid.iData.eOk:
    print('指定图层上有',len(entityList),'个实体')
    for entity in entityList:
    entity.setHighLight(True)
    pid.iDataRegen(pid.Rect(),True)
    del entityList

    pid.iDataExcuteCommand('undo;1') #执行undo命令,带的参数1表示回退一步,效果同命令行输入一样


    #在指定区域内选择IDATA图层上的实体,框选
    rect=pid.Rect(0,0,250,250)
    e,entityList=pid.iDataSSGetRect(rect,pid.iData.ContainsItemShape,['IDATA'])
    if e==pid.iData.eOk:
    print('在指定区域中找到',len(entityList),'个实体')


    #在指定区域内选择IDATA图层上的实体,交选
    rect=pid.Rect(0,0,250,250)
    e,entityList=pid.iDataSSGetRect(rect,pid.iData.IntersectItemsShape,['IDATA'])
    if e==pid.iData.eOk:
    print('在指定区域中找到',len(entityList),'个实体')

    #这里除了iDataSSGetRect还有iDataSSGetPolgon,通过多边形来选择

    #获取图面中被选中的实体
    e,entityList=pid.iDataGetSelectedEntitys()
    if e==pid.iData.eOk:
    print('当前图面中选中了',len(entityList),'个实体')
    pid.ClearSelection() #清除图面的选择

cpp-STL

STL-forward_list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <forward_list>

using namespace std;

int main()
{
forward_list<int> arr;
arr.assign({1,3,5,7}); //分配数据
arr.assign(5,10); //分配5个重复的数据(会覆盖之前的)

arr.push_front(1); //在arr前面插入数据
arr.pop_front(); //删除arr前面一个数据,返回值为void

arr.emplace_front(1); //在arr前面插入一个数据

forward_list<int>::iterator a=arr.begin(); //一个指向arr头位置的迭代器指针
arr.emplace_after(a,10); //emplace_after方法需要两个参数,第一个是迭代器指针,第二个是数据,返回一个迭代器指针

arr.insert_after(a,10); //insert_after方法需要两个参数,第一个是迭代器指针,第二个是数据,返回一个迭代器指针

arr.erase_after(a); //删除迭代器指针后的一个数据

arr.remove(1); //删除arr中的所有值为1的数据

arr.remove_if([](int m){return m%2;}) //删除符合函数条件的数据,remove_if参数为一个函数

forward_list<int> arr2;
arr2.assgin({1,5,8,9});
arr.splice_after(arr.begin(),arr2,arr2.begin(),arr2.end()); //用于拼接两个链表,有四个参数,分别是被拼接链表的迭代器指针,拼接的链表,拼接链表的起始位置,拼接链表的终止位置;第三个参数为begin时不包括拼接内容的第一个位置,为before_begin时包括第一个位置

arr.front(); //返回arr第一个元素

/*----------------------------------------------
这里提一下begin、end和cbegin、cend的区别:
四个方法返回的都是一个迭代器指针,但是c开头的方法返回的是const内容,不能通过它们修改容器里的内容,但是可以移动该指针
----------------------------------------------*/

arr.max_size(); //返回arr能存入的元素最大值

arr.resize(n); //修改arr的容量,当前容量大于n,删除大于部分;小于n,用0补全

forward_list<int>::iterator b; //迭代器
for(b=arr.begin();b!=arr.end();++b) //通过迭代器实现遍历
cout<<*b<<" ";

return 0;
}

STL-array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <array>
#include <tuple>

using namespace std;

int main()
{
array<int,5>arr={1,3,5,7,9}; //指定数组的类型和容量,可以之后再指定元素
array<int,5> arr2;

arr2.fill(0); //fill方法将数组内元素全部置为指定值

for(int i=0;i<5;++i)
cout<<arr2[i]<<" ";
cout<<endl;
arr2={4,5,8,6,8};

arr.swap(arr2); //交换两个数组,两个数组容量要相等

for(int i=0;i<5;++i)
cout<<arr[i]<<" "<<arr2[i]<<"\t";
cout<<endl;

for(int i=0;i<5;++i) //at方法可访问指定的位置元素
cout<<"arr.at("<<i<<"): "<<arr.at(i)<<"\tarr["<<i<<"]: "<<arr[i]<<endl;

cout<<"arr.size(): "<<arr.size()<<endl; //返回数组元素总和
cout<<"arr.max_size(): "<<arr.max_size()<<endl; //返回数组最大容量

cout<<"get<0>(arr): "<<get<0>(arr)<<endl; //get方法重载自tuple,arry内没有,指定要访问的位置和相应的数组

cout<<"arr.front(): "<<arr.front()<<endl; //返回数组第一位元素
cout<<"arr.back(): "<<arr.back()<<endl; //返回数组末尾元素

cout<<arr.empty()<<endl; //判断数组是否为空

return 0;
}

STL-vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <vector>

using namespace std;

int main()
{
vector<int> vec;
for(int i=0;i<5;++i)
vec.push_back(i);
for(auto i=vec.begin();i!=vec.end();++i) //迭代器指针
cout<<*i<<" ";
cout<<endl;
for(auto i=vec.cbegin();i!=vec.cend();++i) //const迭代器指针
cout<<*i<<" ";
cout<<endl;
for(auto i=vec.rbegin();i!=vec.rend();++i) //reverse,一个逆序指针
cout<<*i<<" ";
cout<<endl;
for(auto i=vec.crbegin();i!=vec.crend();++i) //const一个逆序指针
cout<<*i<<" ";
cout<<endl;

cout<<vec.size()<<endl; //vec的当前元素总数量
cout<<vec.max_size()<<endl; //vec的能容纳最大元素数量
cout<<vec.capacity()<<endl; //vec当前分配的容量,若超过容量,对成倍增加容量

vec.resize(2); //将vec元素数量置为指定值,多于指定容量部分不会删除,但是通过迭代器无法访问,能通过[]访问,若不多于则用0补全
vec.shrink_to_fit(); //将resize后多于容量的部分删除

cout<<vec.empty()<<endl; //判空

vector<int> vec2;
vec2.reserve(5); //要求vec2至少能填满5个元素,如果默认容量少于指定数值,会再分配默认容量,否则不会再分配(经过reserve后,插入元素的速度会变快)

/***********************************************************
* vector的[]、at、front、back、assgin、push_back、
* pop_back、insert、erase、swap、emplace、emplace_back等方法用法跟array一样
***********************************************************/
vec.clear(); //清空vec

return 0;
}

iData实体

实体

  • iData图面上的点、线、面、文字注记、影像等元素
    分别为iDataPoint、iDataPolyline、iDataPolygon、iDataText、iDataImage,它们都继承自iDataEntity类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    e,entity,point=pid.iDataEntSel('select an entity you want to check: ')
    #iDataEntSel方法用来选择一个实体,返回三参数,后两个分别是实体以及选定点的坐标
    if e==pid.iData.eOk:
    t=entity.type() #实体类型
    print(e)
    print(entity)
    print(point)
    if t==pid.iData.IDATAPOINT:
    print('point')
    elif t==pid.iData.IDATAPOLYLINE:
    print('line')
    elif t==pid.iData.IDATAPOLYGON:
    print('gon')
    elif t==pid.iData.IDATATEXT:
    print('text')
    elif t==pid.iData.IDATAIMAGE:
    print('image')
    else:
    print("I don't know what is this...")


    nodes,bulges=entity.getNodes() #取得实体节点
    #两个参数的类型都是ListList
    print('实体由{}部分组成'.format(nodes.size()))
    for i in range(len(nodes)):
    print('{}部分包含{}个节点'.format(i+1,len(nodes[i])))
    for p in nodes[i]:
    print(p)
    else:
    print('esc')

实体几何

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#创建点实体
geoCode=pid.getDeFaultGeoCode(pid.iData.IDATAPOINT) #获取默认点编码
pos=pid.Point3D(250,250,3.14) #给定点坐标
entity=pid.iDataPoint(geoCode,pos) #创建一个点实体

pid.iDataAppendEntity(entity) #将实体提交到图面
pid.CommitEntity(entity,pid.iData.kAppended) #提交到数据库

pid.iDataRegen(None,True) #刷新图面

-----------------------------------------------------------
#创建线实体
arrNodes=pid.Point3DListList() #节点列表
arrBulges=pid.DoubleListList() #凸度列表,如不需要留空即可

posList=pid.Point3DList() #坐标列表
posList.append(pid.Point3D(0,0,0))
posList.append(pid.Point3D(1000,250,0))
posList.append(pid.Point3D(500,1000,0))

arrNodes.append(posList)

geoCode=pid.getDeFaultGeoCode(pid.iData.IDATAPOLYLINE)
entity=pid.iDataPolyline(geoCode,arrNodes,arrBulges)

pid.iDataAppendEntity(entity) #实体提交到图面
pid.CommitEntity(entity,pid.iData.kAppended) #实体提交到数据库
pid.iDataRegen(None,True) #刷新图面

-----------------------------------------------------------
#创建面实体
arrNodes=pid.Point3DListList() #节点列表
arrBulges=pid.DoubleListList() #凸度列表,如不需要留空即可

posList=pid.Point3DList() #坐标列表
#构建多边形需要闭合
posList.append(pid.Point3D(0,0,0))
posList.append(pid.Point3D(250,0,0))
posList.append(pid.Point3D(0,250,0))
posList.append(pid.Point3D(0,0,0))

arrNodes.append(posList)

#获取默认面编码,并创建实体
geoCode=pid.getDeFaultGeoCode(pid.iData.IDATAPOLYGON)
entity=pid.iDataPolygon(geoCode,arrNodes,arrBulges)

pid.iDataAppendEntity(entity) #实体提交到图面
pid.CommitEntity(entity,pid.iData.kAppended) #实体提交到数据库
pid.iDataRegen(None,True) #刷新图面

实体凸度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#创建一个圆
arrNodes=pid.Point3DListList() #节点列表
arrBulges=pid.DoubleListList() #凸度列表,如不需要留空即可

posList=pid.Point3DList() #坐标列表
posList.append(pid.Point3D(0,0,0))
posList.append(pid.Point3D(300,0,0))
posList.append(pid.Point3D(0,0,0))
arrNodes.append(posList)

#这里使用凸度控制弧线,凸度=tan(圆心角)/4
bList=pid.DoubleList()
#bList.fromList([1,1,0])
bList.append(1)
bList.append(1)
bList.append(0)
arrBulges.append(bList)

#获取默认线编码,并创建实体
geoCode=pid.getDeFaultGeoCode(pid.iData.IDATAPOLYLINE)
entity=pid.iDataPolygon(geoCode,arrNodes,arrBulges)

pid.iDataAppendEntity(entity) #实体提交到图面
pid.CommitEntity(entity,pid.iData.kAppended) #实体提交到数据库
pid.iDataRegen(None,True) #刷新图面

实体属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
e,entity,point=pid.iDataEntSel('Select an entity')
if e==pid.iData.eOk:
propertys=entity.getXDataAll() #实体所有扩展属性
bz=entity.getXData('WYBZ') #取出特定扩展属性
if bz.type()==pid.Variant.Type_Invalid:
print('选中实体不存在WYBZ字段')
print('原始值: '+entity.getXData('GD').toString())
bz_new=pid.Variant()
bz_new.setValue('设置新值')
entity.setXData('WYBZ',bz_new) #设置新值
print('修改值: '+entity.getXData('GD').toString())
entity.setSelected(True) #让选择的实体显示出来
pid.COmmitEntity(entity,pid.iData.kModified) #修改提交到数据库
pid.iDataRegen(None,True) #刷新图面


print('The selected entity has {} propertys'.format(len(propertys)))
for k,v in propertys:
print(k,': ',v.toString())
p=propertys['CODE']
print(p)
#print(propertys['CODE'].toString()) #同样为取属性
else:
print('esc')
  • 在iData中增删改了实体都需要提交才能生效,分别是提交到图面(按需调用iDataAppendEntity、iDataEntDel)、提交到数据库(调用Commit方法)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    #点实体实例
    count=100
    entityList=pid.iDataEntityList()
    geoCode=pid.getDeFaultGeoCode(pid.iData.IDATAPOINT)
    for i in range(count):
    for j in range(count):
    pos=pid.Point3D(i*10,j*10,0)
    point=pid.iDataPoint(geoCode,pos)
    entityList.append(point)
    #提交实体到图面
    err=pid.iDataAppendEntity(entityList)
    if err==pid.iData.eOk:
    print('添加了{}个实体到屏幕'.format(entityList.size()))
    err=pid.CommitEntity(entityList,pid.iData.kAppended)
    if err==pid.iData.eOk:
    print('添加了{}个实体到数据库'.format(entityList.size()))
    pid.iDataRegen(None,True)

    #线实体实例
    arrNodes=pid.Point3DListList()
    arrBulges=pid.DoubleListList()

    #曲线第一部分,绘制正弦曲线
    posList=pid.Point3DList()
    for i in range(0,628,2):
    y=100*math.sin(i/100.0)
    pos=pid.Point3D(i,y,0)
    posList.append(pos)
    arrNodes.append(posList)

    #曲线第一部分,绘制余弦曲线
    posList=pid.Point3DList()
    for i in range(0,628,2):
    y=100*math.cos(i/100.0)
    pos=pid.Point3D(i,y,0)
    posList.append(pos)
    arrNodes.append(posList)

    geoCode=pid.getDeFaultGeoCode(pid.iData.IDATAPOLYLINE)
    entityList=pid.iDataPolyline(geoCode,arrNodes,arrBulges)

    #提交实体到图面
    err=pid.iDataAppendEntity(entityList)
    err=pid.CommitEntity(entityList,pid.iData.kAppended)

    #添加说明文字
    geoCode=pid.getDeFaultGeoCode(pid.iData.IDATATEXT)
    entity=pid.iDataText(geoCode,pid.Point3D(10,-10,0),'y=sin(x)') #设置文字注记的位置
    entity.setFamily('微软雅黑') #设置字体
    entity.setPointSizeF(18*4.0654228942042955) #设置字高(需要乘以一个固定比例)
    entity.setItalic(True)
    pid.iDataAppendEntity(entity)
    pid.CommitEntity(entity,pid.iData.kAppended)

    pid.iDataRegen(None,True)