Alt + Shift + Enter 全屏,第二次退出全屏
Ctrl + E,D
Ctrl + K,D 格式化当前所有代码
Ctrl + E,F
Ctrl + K,F 格式化选中代码
Ctrl + E,C
Ctrl + K,C 批量注释
Ctrl + E,U
Ctrl + K,U 批量取消注释
Ctrl + Shift + U
Ctrl + U
对选定的字符串进行大写小写的切换.
将光标放在各种成对出现的符号前,按:Ctrl + ] 可实现光标在配对符号间切换,如() <> {}
同上:Ctrl + Shift + ] 实现选中,这样就不用再去找{的结尾在哪了。
Ctrl + Shift + F9删除所有断点
Ctrl + Shift + L删除当前行
F7 从设计模式到代码模式
Shift + F7 从代码模式到设计模式
在任意位置:Ctrl + Enter,当前行上边加一空行,同时光标定位
在任意位置:Ctrl + Shift + Enter,当前行下边加一空行,同时光标定位
Ctrl + K + K :收藏(Bookmark)
F5调试
Shift + F5 退出调试
Ctr+G 查找某行。
Ctrl+R+E 封装字段
调试快捷键
F6: 生成解决方案
Ctrl+F6: 生成当前项目
F7: 查看代码
Shift+F7: 查看窗体设计器
F5: 启动调试
Ctrl+F5: 开始执行(不调试)
Shift+F5: 停止调试
Ctrl+Shift+F5: 重启调试
F9: 切换断点
Ctrl+F9: 启用/停止断点
Ctrl+Shift+F9: 删除全部断点
F10: 逐过程
Ctrl+F10: 运行到光标处
F11: 逐语句编辑快捷键
Shift+Alt+Enter: 切换全屏编辑
Ctrl+B,T / Ctrl+K,K: 切换书签开关
Ctrl+B,N / Ctrl+K,N: 移动到下一书签
Ctrl+B,P: 移动到上一书签
Ctrl+B,C: 清除全部标签
Ctrl+I: 渐进式搜索
Ctrl+Shift+I: 反向渐进式搜索
Ctrl+F: 查找
Ctrl+Shift+F: 在文件中查找
F3: 查找下一个
Shift+F3: 查找上一个
Ctrl+H: 替换
Ctrl+Shift+H: 在文件中替换
Alt+F12: 查找符号(列出所有查找结果)
Ctrl+Shift+V: 剪贴板循环
Ctrl+左右箭头键: 一次可以移动一个单词
Ctrl+上下箭头键: 滚动代码屏幕,但不移动光标位置。
Ctrl+Shift+L: 删除当前行
Ctrl+M,M: 隐藏或展开当前嵌套的折叠状态
Ctrl+M,L: 将所有过程设置为相同的隐藏或展开状态
Ctrl+M,P: 停止大纲显示
Ctrl+E,S: 查看空白
Ctrl+E,W: 自动换行
Ctrl+G: 转到指定行
Shift+Alt+箭头键: 选择矩形文本
Alt+鼠标左按钮: 选择矩形文本
Ctrl+Shift+U: 全部变为大写
Ctrl+U: 全部变为小写
代码快捷键
Ctrl+J / Ctrl+K,L: 列出成员
Ctrl+Shift+空格键 / Ctrl+K,P: 参数信息
Ctrl+K,I: 快速信息
Ctrl+E,C / Ctrl+K,C: 注释选定内容
Ctrl+E,U / Ctrl+K,U: 取消选定注释内容
Ctrl+K,M: 生成方法存根
Ctrl+K,X: 插入代码段
Ctrl+K,S: 插入外侧代码
F12: 转到所调用过程或变量的定义
窗口快捷键
Ctrl+W,W: 浏览器窗口
Ctrl+W,S: 解决方案管理器
Ctrl+W,C: 类视图
Ctrl+W,E: 错误列表
Ctrl+W,O: 输出视图
Ctrl+W,P: 属性窗口
Ctrl+W,T: 任务列表
Ctrl+W,X: 工具箱
Ctrl+W,B: 书签窗口
Ctrl+W,U: 文档大纲
Ctrl+D,B: 断点窗口
Ctrl+D,I: 即时窗口
Ctrl+Tab: 活动窗体切换
Ctrl+Shift+N: 新建项目
Ctrl+Shift+O: 打开项目
Ctrl+Shift+S: 全部保存
Shift+Alt+C: 新建类
Ctrl+Shift+A: 新建项
2011年6月26日星期日
Merge Sort
void mergeSort(int *sortedArr, int start, int end)
{
if(start == end)
{
return;
}
int mid = (int)((start + end) / 2);
mergeSort(sortedArr, start, mid);
mergeSort(sortedArr, mid + 1, end );
int *tempArr = new int[end - start + 1];
int i = start;
int j = mid + 1;
int k = 0;
while(i <= mid && j <= end)
{
if(sortedArr[i] < sortedArr[j])
{
tempArr[k] = sortedArr[i];
i++; k++;
}
else
{
tempArr[k] = sortedArr[j];
j++; k++;
}
}
if(i == mid + 1)
{
for(; j <= end; j++, k++)
tempArr[k] = sortedArr[j];
}
else
{
for(; i <= mid; i++, k++)
tempArr[k] = sortedArr[i];
}
k = 0;
for(int i = start; i <= end; i++, k++)
sortedArr[i] = tempArr[k];
delete[] tempArr;
}
{
if(start == end)
{
return;
}
int mid = (int)((start + end) / 2);
mergeSort(sortedArr, start, mid);
mergeSort(sortedArr, mid + 1, end );
int *tempArr = new int[end - start + 1];
int i = start;
int j = mid + 1;
int k = 0;
while(i <= mid && j <= end)
{
if(sortedArr[i] < sortedArr[j])
{
tempArr[k] = sortedArr[i];
i++; k++;
}
else
{
tempArr[k] = sortedArr[j];
j++; k++;
}
}
if(i == mid + 1)
{
for(; j <= end; j++, k++)
tempArr[k] = sortedArr[j];
}
else
{
for(; i <= mid; i++, k++)
tempArr[k] = sortedArr[i];
}
k = 0;
for(int i = start; i <= end; i++, k++)
sortedArr[i] = tempArr[k];
delete[] tempArr;
}
2011年6月24日星期五
【转】解决hash冲突的策略
1 Overview
Open addressing和Chaining是两种不同的解决hash冲突的策略。当多个不同的key被映射到相同的slot时,chaining方式采用链表保存所有的value。而Open addressing则尝试在该slot的邻近位置查找,直到找到对应的value或者空闲的slot, 这个过程被称作probing。常见的probing策略有Linear probing,Quadratic probing和Double hashing。
2 Chaining
2.1 Chaining in java.util.HashMap
在分析open addressing策略之前,首先简单介绍一下大多数的Java 核心集合类采用的chaining策略,以便比较。 java.util.HashMap有一个Entry[]类型的成员变量table,其每个非null元素都是一个单向链表的表头。
- put():如果存在hash冲突,那么对应的table元素的链表长度会大于1。
- get():需要对链表进行遍历,在遍历的过程中不仅要判断key的hash值是否相等,还要判断key是否相等或者equals。
- 对于put()和get()操作,如果其参数key为null,那么HashMap会有些特别的优化。
Chaining策略的主要缺点是需要通过Entry保存key,value以及指向链表下个节点的引用(Map.Entry就有四个成员变量),这意味着更多的内存使用(尤其是当key,value本身使用的内存很小时,额外使用的内存所占的比例就显得比较大)。此外链表对CPU的高速缓存不太友好。
3 Open Addressing
3.1 Probing
3.1.1 Linear probing
两次查找位置的间隔为一固定值,即每次查找时在原slot位置的基础上增加一个固定值(通常为1),例如:P = (P + 1) mod SLOT_LENGTH。其最大的优点在于计算速度快,另外对CPU高速缓存更友好。其缺点也非常明显:
假设key1,key2,key3的hash code都相同并且key1被映射到slot(p),那么在计算key2的映射位置时需要查找slot(p), slot(p+1),计算key3的映射位置时需要查找slot(p), slot(p+1),slot(p+2)。也就是说对于导致hash冲突的所有key,在probing过程中会重复查找以前已经查找过的位置,这种现象被称为clustering。
3.1.2 Quadratic probing
两次查找位置的间隔线性增长,例如P(i) = (P + c1*i + c2*i*i) mod SLOT_LENGTH,其中c1和c2为常量且c2不为0(如果为0,那么降级为Linear probing)。 Quadratic probing的各方面性能介于Linear probing和Double hashing之间。
3.1.3 Double hashing
两次查找位置的间隔为一固定值,但是该值通过另外一个hash算法生成,例如P = (P + INCREMENT(key)) mod SLOT_LENGTH,其中INCREMENT即另外一个hash算法。以下是个简单的例子:
H(key) = key mod 10
INCREMENT(key) = 1 + (key mod 7)
P(15): H(15) = 5;
P(35): H(35) = 5, 与P(15)冲突,因此需要进行probe,位置是 (5 + INCREMENT(35)) mod 10 = 6
P(25): H(25) = 5, 与P(15)冲突,因此需要进行probe,位置是 (5 + INCREMENT(25)) mod 10 = 0
INCREMENT(key) = 1 + (key mod 7)
P(15): H(15) = 5;
P(35): H(35) = 5, 与P(15)冲突,因此需要进行probe,位置是 (5 + INCREMENT(35)) mod 10 = 6
P(25): H(25) = 5, 与P(15)冲突,因此需要进行probe,位置是 (5 + INCREMENT(25)) mod 10 = 0
P(75): H(75) = 5, 与P(15)冲突,因此需要进行probe,位置是 (5 + INCREMENT(75)) mod 10 = 1
从以上例子可以看出,跟Linear probing相比,减少了重复查找的次数。
3.2 Load Factor
基于open addressing的哈希表的性能对其load factor属性值非常敏感。如果该值超过0.7 (Trove maps/sets的默认load factor是0.5),那么性能会下降的非常明显。由于hash冲突导致的probing次数跟(loadFactor) / (1 - loadFactor)成正比。当loadFactor为1时,如果哈希表中的空闲slot非常少,那么可能会导致probing的次数非常大。
2011年6月21日星期二
<转>二叉树的层次遍历
1. 首先将root节点enqueue。 2.循环, 直到queue为空: 从queue中dequeue一个节点并访问。如果此节点存在left child, 将其enqueue。如果存在right child, 将其enqueue。 /*按层次遍历二叉树,借助队列来实现,为简便编程,借助数组实现队列功能*/ #include "stdio.h" #include "conio.h" #include "stdlib.h" typedef char TElemType; typedef struct BiTNode { TElemType data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; /*先序创建二叉树*/ void createBiTree(BiTree *T) { char c; scanf("%c",&c); if(c!='#') { *T=(BiTree)malloc(sizeof(BiTNode)); (*T)->data=c; createBiTree(&((*T)->lchild)); createBiTree(&((*T)->rchild)); } else *T=NULL; } /*先序遍历二叉树*/ void preOrder(BiTree T) { if(T) { printf("%c",T->data); preOrder(T->lchild); preOrder(T->rchild); } } /*按层遍历*/ void levelTraverse(BiTree T) { BiTree Queue[20]; BiTree p; int front=0,rear=0; /*表示队头指针和队尾指针*/ if(T) { p=T; /*根指针入队*/ Queue[rear]=p; rear=(rear+1)%20; /*队尾指针加一对20取余,可实现循环队列,合理利用空间*/ while(front!=rear) /*队不空*/ { p=Queue[front]; /*出队,将值赋给p*/ printf("%c",p->data); front=(front+1)%20; if(p->lchild) /*如果p有左子树,将左子树入队*/ { Queue[rear]=p->lchild; rear=(rear+1)%20; } if(p->rchild) /*如果p有右子树,将右子树入队*/ { Queue[rear]=p->rchild; rear=(rear+1)%20; } } } } main() { BiTree T; createBiTree(&T); preOrder(T); printf("\n"); levelTraverse(T); getch(); } |
<转>关于C++中函数指针的使用(包含对typedef用法的讨论)
(一)简单的函数指针的应用。
//形式1:返回类型(*函数名)(参数表)
char (*pFun)(int);
char glFun(int a){ return;}
void main()
{
pFun = glFun;
(*pFun)(2);
}
第一行定义了一个指针变量pFun。首先我们根据前面提到的“形式1”认识到它是一个指向某种函数的指针,这种函数参数是一个int型,返回值是char类型。只有第一句我们还无法使用这个指针,因为我们还未对它进行赋值。
第二行定义了一个函数glFun()。该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数——函数的函数名实际上就是一个指针,函数名指向该函数的代码在内存中的首地址。
然后就是可爱的main()函数了,它的第一句您应该看得懂了——它将函数glFun的地址赋值给变量pFun。main()函数的第二句中“*pFun”显然是取pFun所指向地址的内容,当然也就是取出了函数glFun()的内容,然后给定参数为2。
(二)使用typedef更直观更方便。
//形式2:typedef 返回类型(*新类型)(参数表)
typedef char (*PTRFUN)(int);
PTRFUN pFun;
char glFun(int a){ return;}
void main()
{
pFun = glFun;
(*pFun)(2);
}
typedef的功能是定义新的类型。第一句就是定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。后面就可以像使用int,char一样使用PTRFUN了。
第二行的代码便使用这个新类型定义了变量pFun,此时就可以像使用形式1一样使用这个变量了。
(三)在C++类中使用函数指针。
//形式3:typedef 返回类型(类名::*新类型)(参数表) class CA
{
public:
char lcFun(int a){ return; }
};
CA ca;
typedef char (CA::*PTRFUN)(int);
PTRFUN pFun;
void main()
{
pFun = CA::lcFun;
ca.(*pFun)(2);
}
在这里,指针的定义与使用都加上了“类限制”或“对象”,用来指明指针指向的函数是那个类的这里的类对象也可以是使用new得到的。比如:
CA *pca = new CA;
pca->(*pFun)(2);
delete pca;
而且这个类对象指针可以是类内部成员变量,你甚至可以使用this指针。比如:
类CA有成员变量PTRFUN m_pfun;
void CA::lcFun2()
{
(this->*m_pFun)(2);
}
一句话,使用类成员函数指针必须有“->*”或“.*”的调用。
//形式1:返回类型(*函数名)(参数表)
char (*pFun)(int);
char glFun(int a){ return;}
void main()
{
pFun = glFun;
(*pFun)(2);
}
第一行定义了一个指针变量pFun。首先我们根据前面提到的“形式1”认识到它是一个指向某种函数的指针,这种函数参数是一个int型,返回值是char类型。只有第一句我们还无法使用这个指针,因为我们还未对它进行赋值。
第二行定义了一个函数glFun()。该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数——函数的函数名实际上就是一个指针,函数名指向该函数的代码在内存中的首地址。
然后就是可爱的main()函数了,它的第一句您应该看得懂了——它将函数glFun的地址赋值给变量pFun。main()函数的第二句中“*pFun”显然是取pFun所指向地址的内容,当然也就是取出了函数glFun()的内容,然后给定参数为2。
(二)使用typedef更直观更方便。
//形式2:typedef 返回类型(*新类型)(参数表)
typedef char (*PTRFUN)(int);
PTRFUN pFun;
char glFun(int a){ return;}
void main()
{
pFun = glFun;
(*pFun)(2);
}
typedef的功能是定义新的类型。第一句就是定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。后面就可以像使用int,char一样使用PTRFUN了。
第二行的代码便使用这个新类型定义了变量pFun,此时就可以像使用形式1一样使用这个变量了。
(三)在C++类中使用函数指针。
//形式3:typedef 返回类型(类名::*新类型)(参数表) class CA
{
public:
char lcFun(int a){ return; }
};
CA ca;
typedef char (CA::*PTRFUN)(int);
PTRFUN pFun;
void main()
{
pFun = CA::lcFun;
ca.(*pFun)(2);
}
在这里,指针的定义与使用都加上了“类限制”或“对象”,用来指明指针指向的函数是那个类的这里的类对象也可以是使用new得到的。比如:
CA *pca = new CA;
pca->(*pFun)(2);
delete pca;
而且这个类对象指针可以是类内部成员变量,你甚至可以使用this指针。比如:
类CA有成员变量PTRFUN m_pfun;
void CA::lcFun2()
{
(this->*m_pFun)(2);
}
一句话,使用类成员函数指针必须有“->*”或“.*”的调用。
<转>dynamic_cast使用方式
c++提供了四种新的cast机制,分别为static_cast, const_cast, dynamic_cast和reinterpret_cast。虽然也支持c中使用一对圆括号来cast,但是由于c++与c最大的区别是c++增加了类的概念,因此在子类与父类之间进行cast的时候,使用c的cast方式是无法保证其正确性的,因此c++提供了新的cast机制(虽然比较丑陋而且需要敲打更多的code,但是提供了安全性):dynamic_cast。
对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。因此可以用来在运行期间进行类型判断。如下述演示代码。
class CShape{...}
class CCircle:public CShape{...}
class CRectangle: public CShape{...}
CShape * pShape;
... //do something
if (dynamic_cast<CCircle * > (pShape))
{
//process the Circle object
}
else if (dynamic_cast<CRectangle * > (pShape))
{
//process the Rectangle object
}
else
{
//error, just shape object
}
如下面代码所示,将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理,即会作一定的判断。由于这会用到RTTI技术,因此需要启动“运行时类型信息”这一选项,而在VC.net 2003中默认是关闭的,所以需要人为的启动这一选项,如图1所示。否则编译器会报下面的警告:
warning C4541: “dynamic_cast”用在了带 /GR- 的多态类型“CBasic”上;可能导致不可预知的行为
从而导致程序在运行时发生异常。
但是dynamic_cast在将父类cast到子类时,父类必须要有虚函数。例如在图1中的代码中将CBasic类中的test函数不定义成virtual时,编译器会报错:
error C2683: dynamic_cast : “CBasic”不是多态类型
这是由于有了多态后,将子类对象赋值给父类对象才有意义。这样子类与父类之间的cast才有意义。
当然如果使用dynamic_cast来直接将子类cast到父类(向上cast),那就无所谓了。即父类不需要一定有虚函数,也不需要开启“运行时类型信息”这一选项。
#include <iostream>
using namespace std;
class CBasic
{
public:
virtual int test()...{return 0;}
};
class CDerived : public CBasic
{
public:
virtual int test()...{ return 1;}
};
int main()
{
CBasic cBasic;
CDerived cDerived;
CBasic * pB1 = new CBasic;
CBasic * pB2 = new CDerived;
//dynamic cast failed, so pD1 is null.
CDerived * pD1 = dynamic_cast<CDerived * > (pB1);
//dynamic cast succeeded, so pD2 points to CDerived object
CDerived * pD2 = dynamic_cast<CDerived * > (pB2);
//dynamci cast failed, so throw an exception.
// CDerived & rD1 = dynamic_cast<CDerived &> (*pB1);
//dynamic cast succeeded, so rD2 references to CDerived object.
CDerived & rD2 = dynamic_cast<CDerived &> (*pB2);
return 0;
}
对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。因此可以用来在运行期间进行类型判断。如下述演示代码。
class CShape{...}
class CCircle:public CShape{...}
class CRectangle: public CShape{...}
CShape * pShape;
... //do something
if (dynamic_cast<CCircle * > (pShape))
{
//process the Circle object
}
else if (dynamic_cast<CRectangle * > (pShape))
{
//process the Rectangle object
}
else
{
//error, just shape object
}
如下面代码所示,将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理,即会作一定的判断。由于这会用到RTTI技术,因此需要启动“运行时类型信息”这一选项,而在VC.net 2003中默认是关闭的,所以需要人为的启动这一选项,如图1所示。否则编译器会报下面的警告:
warning C4541: “dynamic_cast”用在了带 /GR- 的多态类型“CBasic”上;可能导致不可预知的行为
从而导致程序在运行时发生异常。
但是dynamic_cast在将父类cast到子类时,父类必须要有虚函数。例如在图1中的代码中将CBasic类中的test函数不定义成virtual时,编译器会报错:
error C2683: dynamic_cast : “CBasic”不是多态类型
这是由于有了多态后,将子类对象赋值给父类对象才有意义。这样子类与父类之间的cast才有意义。
当然如果使用dynamic_cast来直接将子类cast到父类(向上cast),那就无所谓了。即父类不需要一定有虚函数,也不需要开启“运行时类型信息”这一选项。
#include <iostream>
using namespace std;
class CBasic
{
public:
virtual int test()...{return 0;}
};
class CDerived : public CBasic
{
public:
virtual int test()...{ return 1;}
};
int main()
{
CBasic cBasic;
CDerived cDerived;
CBasic * pB1 = new CBasic;
CBasic * pB2 = new CDerived;
//dynamic cast failed, so pD1 is null.
CDerived * pD1 = dynamic_cast<CDerived * > (pB1);
//dynamic cast succeeded, so pD2 points to CDerived object
CDerived * pD2 = dynamic_cast<CDerived * > (pB2);
//dynamci cast failed, so throw an exception.
// CDerived & rD1 = dynamic_cast<CDerived &> (*pB1);
//dynamic cast succeeded, so rD2 references to CDerived object.
CDerived & rD2 = dynamic_cast<CDerived &> (*pB2);
return 0;
}
订阅:
博文 (Atom)