《剑指offer》读书笔记(第1章)

在读《剑指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. // 1:构造二叉查找树;   
  2. // 2:中序遍历二叉查找树,因此结点按从小到大顺序访问,假设之前访问过的结点已经调整为一个双向链表,那么   
  3. //       只需要将当前结点连接至双向链表的最后一个结点即可,访问完后,双向链表也就调整完了   
  4. #include <iostream>   
  5. using namespace std;   
  6. struct BSTreeNode   
  7. {   
  8.     int m_nValue; // value of node   
  9.     BSTreeNode *m_pLeft; // left child of node   
  10.     BSTreeNode *m_pRight; // right child of node   
  11. };   
  12.     
  13. void addBSTreeNode(BSTreeNode *&pCurrent,int value);   
  14. void inOrderBSTree(BSTreeNode* pBSTree);   
  15. void convertToDoubleList(BSTreeNode* pCurrent);   
  16.     
  17. BSTreeNode *pHead=NULL;//指向循环队列头结点   
  18. BSTreeNode *pIndex=NULL;//指向前一个结点   
  19.     
  20. int main()   
  21. {   
  22.     BSTreeNode *pRoot=NULL;   
  23.     addBSTreeNode(pRoot,10);   
  24.     addBSTreeNode(pRoot,6);   
  25.     addBSTreeNode(pRoot,14);   
  26.     addBSTreeNode(pRoot,4);   
  27.     addBSTreeNode(pRoot,8);   
  28.     addBSTreeNode(pRoot,12);   
  29.     addBSTreeNode(pRoot,16);   
  30.     inOrderBSTree(pRoot);   
  31.     system("pause");  
  32.     return 0;   
  33. }   
  34. /************************************************************************/  
  35. /* 建立二叉排序树                                                               */  
  36. void addBSTreeNode(BSTreeNode *&pCurrent,int value)//在这个函数中会要改变指针值,一定要记得使用引用传递   
  37. {   
  38.     if (pCurrent==NULL)   
  39.     {   
  40.         BSTreeNode* pBSTree=new BSTreeNode();   
  41.         pBSTree->m_nValue=value;   
  42.         pBSTree->m_pLeft=NULL;   
  43.         pBSTree->m_pRight=NULL;   
  44.         pCurrent=pBSTree;   
  45.     }   
  46.     else if (pCurrent->m_nValue<value)   
  47.     {   
  48.         addBSTreeNode(pCurrent->m_pRight,value);   
  49.     }   
  50.     else if (pCurrent->m_nValue>value)   
  51.     {   
  52.         addBSTreeNode(pCurrent->m_pLeft,value);   
  53.     }   
  54.     else  
  55.     {   
  56.         cout<<"node repeated"<<endl;   
  57.     }   
  58.     
  59. }   
  60. /************************************************************************/  
  61.     
  62. /************************************************************************/  
  63. /* 中序遍历二叉树,同时调整结点指针                                                                     */  
  64. void inOrderBSTree(BSTreeNode* pBSTree)   
  65. {   
  66.     
  67.     if (NULL==pBSTree)   
  68.     {   
  69.         return;   
  70.     }   
  71.     if (NULL!=pBSTree->m_pLeft)   
  72.     {   
  73.         inOrderBSTree(pBSTree->m_pLeft);   
  74.     }   
  75.     
  76.     //  if (NULL!=pBSTree)   
  77.     //  {   
  78.     //      cout<<pBSTree->m_nValue;   
  79.     //  }   
  80.     convertToDoubleList(pBSTree);   
  81.     
  82.     if (NULL!=pBSTree->m_pRight)   
  83.     {   
  84.         inOrderBSTree(pBSTree->m_pRight);   
  85.     }   
  86.     
  87. }   
  88. /************************************************************************/  
  89.     
  90. /************************************************************************/  
  91. /* 调整结点指针                                                                   */  
  92. void convertToDoubleList(BSTreeNode* pCurrent)   
  93. {   
  94.     pCurrent->m_pLeft=pIndex;//使当前结点的左指针指向双向链表中最后一个结点   
  95.     if (NULL==pIndex)//若最后一个元素不存在,此时双向链表尚未建立,因此将当前结点设为双向链表头结点   
  96.     {   
  97.         pHead=pCurrent;   
  98.     }   
  99.     else//使双向链表中最后一个结点的右指针指向当前结点   
  100.     {   
  101.         pIndex->m_pRight=pCurrent;   
  102.     }   
  103.     
  104.     pIndex=pCurrent;//将当前结点设为双向链表中最后一个结点   
  105.     
  106.     cout<<pCurrent->m_nValue<<" ";   
  107.     
  108. }   
  109. /************************************************************************/  


http://blog.sina.com.cn/s/blog_68b6063501019pql.html

http://blog.chinaunix.net/uid-1844931-id-3308827.html


10.求斐波拉切数列




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值