在读《剑指offer》时候遇到一些知识点的整理,查漏补缺:
1.(剑P11)传入值参数和传入引用参数有什么区别,什么时候需要为传入的引用参数加上const?
(C++P274)什么时候应该使用引用,什么时候应该使用指针,什么时候应该按值传递?
首先,为什么要使用指针或者引用?这样可以提高程序的效率,节省复制结构所需要的时间和空间。
2.(剑P11)说到“哈希表”,因为之前没有接触过,在网上找了些资料学习。
折半查找,二叉排序树查找,B-树查找
哈希函数的构造方法:直接定址法,数字分析法,平方取中法,折叠法,除留余数法,随机数法
除留余数法:最简单,最常用,取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址。
经验:可以选择p为质数或者不包含小于20的质因数的合数。
随机数法:当关键字长不等时
考虑因素:计算哈希函数需要的时间,关键字的长度,哈希表的大小,关键字的分布情况,记录的查找频率
处理冲突的方法:开放地址法,再哈希法,链地址法,建立一个公共溢出区
http://blog.csdn.net/feixiaoxing/article/details/6885657
3.查找
二分查找法O(logN)
http://www.cnblogs.com/xwdreamer/archive/2012/05/07/2487246.html
http://blog.csdn.net/21aspnet/article/details/1539685
1.输入不符合标准
2. mid的选择
3.效率
一一一一一一一一一一一一一一一一一一一一一一
#include<iostream>
#include<stdlib.h>
using namespace std;
//不适用递归,如果存在返回数组位置,不存在则返回-1
int BinarySearch(int arry[],int len,int value)
{
//如果传入的数组为空或者数组长度<=0那么就返回-1。防御性编程
if(arry==NULL||len<=0)
return -1;
int start=0;
int end=len-1;
while(start<=end)//判断清是否有=
{
int mid=start+(end-start)/2; //使用 (start + end) / 2 会有整数溢出的问题
if(arry[mid]==value)
return mid;
else if(value<arry[mid])
end=mid-1;
else
start=mid+1;
}
return -1;
}
一一一一一一一一一一一一一一一一一一一一一一
//改进思路:1.不要传参,而是传引用调用,减少垃圾
// 2.使用模板
int BinarySearchRecursion(int arry[],int value,int start,int end)
{
if(start>end)
return -1;
int mid=start+(end-start)/2;
if(arry[mid]==value)
return mid;
else if(value<arry[mid])
return BinarySearchRecursion(arry,value,start,mid-1);
else
return BinarySearchRecursion(arry,value,mid+1,end);
}
int BinarySearchRecursion(int arry[],int len,int value)
{
//如果传入的数组为空或者数组长度<=0那么就返回-1。防御性编程
if(arry==NULL||len<=0)
return -1;
return BinarySearchRecursion(arry,value,0,len-1);
}
void main()
{
int arry[]={1,2,3,4,5,6,7,8};
int len=sizeof(arry)/sizeof(int);
int index=BinarySearch(arry,len,4);
cout<<"index:"<<index<<endl;
int index2=BinarySearchRecursion(arry,len,9);
cout<<"index2:"<<index2<<endl;
system("pause");
}
4.二叉树遍历(循环,递归)
5.链表,树,栈,队列,哈希表,二叉树
6.动态规划算法,贪婪算法
7.把一个字符串转换成整数。
1.字符串很长超过了整数的长度
2.输入有错误(非数字字符串)怎么处理(当输入的字符串是一个空指针或者含有非法的字符,还有如何确定返回值)
另外一种思路,我们可以返回一个布尔值来指示输入是否有效,而把转换后的整数放到参数列表中以引用或者指针的形式传入。于是我们就可以声明如下:boolStrToInt(constchar *str,int& num);但是不够直观,无法直接使用返回值,如何在保证直观的前提下当碰到非法输入的时候通知用户呢?一种解决方案就是定义一个全局变量,每当碰到非法输入的时候,就标记该全局变量。用户在调用这个函数之后,就可以检验该全局变量flag来判断转换是不是成功。
3.正负号处理
4.(同1)考虑最大的正整数和最小的负整数以及溢出
5.怎么转换
在linux字符界面下,C语言程序对应的汇编代码:gcc –S var_return_in_fun.c,得到var_return_in_fun.s文件,打开文件查看汇编代码(由C语言转变成汇编语言的方法)编译C语言源文件时可不为gcc添加加-O2优化参数,不然在汇编代码中会看不到子函数调用的call指令。windows下采用的Intel的汇编格式,linux采取的是AT&T汇编格式。
关于return局部变量的博客介绍,讲的很详细很好:http://blog.csdn.net/misskissc/article/details/10757975
自己代码:
int StrToInt (char* String)//很多问题没有考虑
{
int number=0;
if(NULL==*String)
{
printf("input error");
return;
}
while(*String!='\0')
{
number=number*10+*String-'0';
String++;
}
return number;
}
参考代码:
enum Status {kValid = 0, kInvalid};
int g_nStatus = kValid;
///
// Convert a string into an integer
///
int StrToInt(const char* str)
{
g_nStatus = kInvalid;
long long num = 0; //考虑溢出的可能
if(str != NULL) //空指针
{
const char* digit = str;
// 判断正负号
bool minus = false;
if(*digit == '+')
digit ++;
else if(*digit == '-')
{
digit ++;
minus = true;
}
// 剩下的字符串是数字
while(*digit != '/0')
{
if(*digit >= '0' && *digit <= '9')
{
num = num * 10 + (*digit - '0');
// 溢出
if(num > std::numeric_limits<int>::max())
{
num = 0;
break;
}
digit ++;
}
// 输入不是数字,是非法输入
else
{
num = 0;
break;
}
}
if(*digit == '/0')
{
g_nStatus = kValid;
if(minus)
num = 0 - num;
}
}
return static_cast<int>(num); // 将num强制转换成int类型
}
在C语言提供的库函数中,函数atoi能够把字符串转换整数。它的声明是int atoi(const char *str)。该函数就是用一个全局变量来标志输入是否合法的。
8.求链表中的倒数第k个结点。
1.空链表
2.k值不正确处理
3.效率,时间复杂度
自己代码:
PNode Find(PNode Phead,unsigned int k)
{
if(NULL==Phead)
{
return NULL;
}
else
{
PNode ptemp=NULL;
ptemp=Phead;
int num=0;
while(ptemp->pnext==NULL)
{
ptemp=ptemp->pnext;
num++;
}
if(k>m)
{
printf("k is wrong!\n");
return NULL;
}
while(n-k)
{
ptemp=Phead;
ptemp=ptemp->pnext;
}
return ptemp;
}
}
分析:该算法时间复杂度为O(n),遍历链表两次,显然效率不高,如果我们在遍历时维持两个指针,第一个指针从链表的头指针开始遍历,在第k-1步之前,第二个指针保持不动;在第k-1步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第k个结点。
这种思路只需要遍历链表一次。对于很长的链表,只需要把每个结点从硬盘导入到内存一次。因此这一方法的时间效率前面的方法要高。
参考代码:
PNode Find(PNode Phead,unsigned int k)
{
PNode pa,pb;
pa=pb=Phead;
int i;
for(i=0;i<k-1;i++)
{
if(pa->pnext!=NULL) //判断当k小于m的时候
{
pa=pa->pnext;
}
else
{
return NULL;
}
}
while(pa->pnext!=NULL)
{
pa=pa->pnext;
pb=pb->pnext;
}
return pb;
}
扩展:和这道题类似的题目还有:输入一个单向链表。如果该链表的结点数为奇数,输出中间的结点;如果链表结点数为偶数,输出中间两个结点前面的一个。
扩展:输入一个单向链表。如果该链表的结点数为奇数,输出中间的结点;如果链表结点数为偶数,输出中间两个结点前面的一个。
思路一:先遍历一遍链表,获得链表节点总数,再根据总节点数的奇偶性输出中间节点。
思路二:两个指针,一快一慢,慢指针每走一步,快指针走两步。当快指针指向节点的下一节点或下一节点的下一节点为空时,慢指针所指即是中间节点。
9.把二叉搜索树转换成排序地双向链表
题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求不能创建任何新的结点,只调整指针的指向。
比如将二元查找树(它首先要是一棵二元树,在这基础上它或者是一棵空树;或者是具有下列性质的二元树: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值; (2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值; (3)左、右子树也分别为二元查找树)
10
/ \
6 14
/ \ / \
4 8 12 16
转换成双向链表
4=6=8=10=12=14=16。
总体思路:递归+中序遍历二叉树。
递归和非递归算法
- // 1:构造二叉查找树;
- // 2:中序遍历二叉查找树,因此结点按从小到大顺序访问,假设之前访问过的结点已经调整为一个双向链表,那么
- // 只需要将当前结点连接至双向链表的最后一个结点即可,访问完后,双向链表也就调整完了
- #include <iostream>
- using namespace std;
- struct BSTreeNode
- {
- int m_nValue; // value of node
- BSTreeNode *m_pLeft; // left child of node
- BSTreeNode *m_pRight; // right child of node
- };
- void addBSTreeNode(BSTreeNode *&pCurrent,int value);
- void inOrderBSTree(BSTreeNode* pBSTree);
- void convertToDoubleList(BSTreeNode* pCurrent);
- BSTreeNode *pHead=NULL;//指向循环队列头结点
- BSTreeNode *pIndex=NULL;//指向前一个结点
- int main()
- {
- BSTreeNode *pRoot=NULL;
- addBSTreeNode(pRoot,10);
- addBSTreeNode(pRoot,6);
- addBSTreeNode(pRoot,14);
- addBSTreeNode(pRoot,4);
- addBSTreeNode(pRoot,8);
- addBSTreeNode(pRoot,12);
- addBSTreeNode(pRoot,16);
- inOrderBSTree(pRoot);
- system("pause");
- return 0;
- }
- /************************************************************************/
- /* 建立二叉排序树 */
- void addBSTreeNode(BSTreeNode *&pCurrent,int value)//在这个函数中会要改变指针值,一定要记得使用引用传递
- {
- if (pCurrent==NULL)
- {
- BSTreeNode* pBSTree=new BSTreeNode();
- pBSTree->m_nValue=value;
- pBSTree->m_pLeft=NULL;
- pBSTree->m_pRight=NULL;
- pCurrent=pBSTree;
- }
- else if (pCurrent->m_nValue<value)
- {
- addBSTreeNode(pCurrent->m_pRight,value);
- }
- else if (pCurrent->m_nValue>value)
- {
- addBSTreeNode(pCurrent->m_pLeft,value);
- }
- else
- {
- cout<<"node repeated"<<endl;
- }
- }
- /************************************************************************/
- /************************************************************************/
- /* 中序遍历二叉树,同时调整结点指针 */
- void inOrderBSTree(BSTreeNode* pBSTree)
- {
- if (NULL==pBSTree)
- {
- return;
- }
- if (NULL!=pBSTree->m_pLeft)
- {
- inOrderBSTree(pBSTree->m_pLeft);
- }
- // if (NULL!=pBSTree)
- // {
- // cout<<pBSTree->m_nValue;
- // }
- convertToDoubleList(pBSTree);
- if (NULL!=pBSTree->m_pRight)
- {
- inOrderBSTree(pBSTree->m_pRight);
- }
- }
- /************************************************************************/
- /************************************************************************/
- /* 调整结点指针 */
- void convertToDoubleList(BSTreeNode* pCurrent)
- {
- pCurrent->m_pLeft=pIndex;//使当前结点的左指针指向双向链表中最后一个结点
- if (NULL==pIndex)//若最后一个元素不存在,此时双向链表尚未建立,因此将当前结点设为双向链表头结点
- {
- pHead=pCurrent;
- }
- else//使双向链表中最后一个结点的右指针指向当前结点
- {
- pIndex->m_pRight=pCurrent;
- }
- pIndex=pCurrent;//将当前结点设为双向链表中最后一个结点
- cout<<pCurrent->m_nValue<<" ";
- }
- /************************************************************************/
http://blog.sina.com.cn/s/blog_68b6063501019pql.html
http://blog.chinaunix.net/uid-1844931-id-3308827.html
10.求斐波拉切数列