剑指offer总结

来源:互联网 发布:linux 共享打印机 编辑:程序博客网 时间:2024/05/20 09:11
1、实现Singleton模式

2、二维数组中的查找:每行从左到右递增,每列从上到下递增,输入一个数,判断数组中是否存在该数
1 2  8 9 
2 4  9 12
4 7 10 13 
6 8 11 15
如输入7:
小于第4列的9,则不可能在第4列;column--
小于第3列的8,则不可能在第3列;column--
大于第二列的2,则row++;
始终比较右上角的数,相等则返回。(每次查找都会剔除一行或者一列)

3、替换空格:
(移动重复,想办法减少重复,只移动一次!)
遍历一遍字符串char[],得知空格数目,就可知道替换后的字符串长度,用双指针p1 p2来从后往前移动字串
p1在p2的前面,指针同步移动(copy字符过去),但是p1遇到空格,p2就往前2步直到p2遇上p1,空格替换完毕

4、从尾到头部打印链表:(先询问是否可以改变链表结构)
   1、不改变:遍历链表,值入栈,遍历结束再值出栈
   2、改变:用头插法重建链表,再顺序遍历链表

5、根据前序和中序重建二叉树:Java代码

6、用2个栈实现队列:Java代码

7、旋转数组求最小:

8、求斐波那契数列第n项
   递归;直接用公式O(1);递推O(n)  类似双指针 把已经得到的数列项保存起来

9、求二进制中的1的个数:
   最优解法:(1100&1011=1000 能去掉最右边的1)
   intNumber(int n ){
      int count=0;
      while(n){
      count++;
      n=(n-1)&n;
    }
  }
常规解法:
while(flag){
   if(n&flag){
     count++;
   flag=flag<<1;
}
}

10、数值的整数次方:
要考虑负数(利用正数来求),0,正数
double f(double base,unsigned int e){
  if(e==0)return 1;
  if(e==1)return base;
  double res=f(base,e>>1);
  res*=res;
  if(e&1==1)res*=base;
  return res; 
}//细节

12、打印1-n的大数(转化为字符)


13、删除节点O(1)  值覆盖p.data=p.next.data;p.next=p.next.next;

14、调整数组使得奇数位于偶数前面(双指针一前一后 判断奇数p&1==1?)

15、链表中的倒数第k个节点(双指针  注意边界)

16、反转链表 (1)双指针 头插法(2)三个指针pPrev pNode pNext 直接反转链表

17、合并2个排序的链表:
ListNode* Merge(ListNode* pHead1,ListNode* pHead2){
if(pHead1==null)return pHead2;
else if (pHead2==null)return pHead1;

ListNode* pMergedHead=null;

if(pHead1->Value<pHead2->Value){
pMergeHead=pHead1;
pMergeHead->next=Merge(pHead->next,pHead2);
}
else {
pMergeHead=pHead2;
pMergeHead->next=Merge(pHead1,pHead->next);
}
return pMergeHead;
}

18、判断树B是否是树A的子结构
struct BinaryTree{
  int Value;
  BinaryTree* left;
  BinaryTree* right;
};
bool HasSubtee(BinaryTreeNode* pRoot1,BinaryTree* pRoot2){
bool result==false;
if(pRoot1!=null&&pRoot2!=null){
result=DoesTree1HaveTree2(pRoot1,pRoot2);
if(!result)
result=HasSubutree(pRoot1->left,pRoot2);
if(!result)
result=HasSubtree(pRoot1->right,pRoot2);
}
return result;
}
bool DoesTree1HaveTree2(BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2){
if(pRoot2==null)return true;
if(pRoot1==null)return false;
if(pRoot1->Value!=pRoot2->Value)return false;
return DoesTree1HaveTree2(pRoot1->left,pRoot->left)&&DoesTree1HaveTree2(pRoot1-


>right,pRoot2->right);

}


19、二叉树的镜像:
void MirrorRecursively(BinaryTreeNode* pNode){
if((pNode==null)||(pNode->left==null&&pNode->right))return;
        //先写递归代码,再添加退出条件
BinaryTreeNode* pTemp=pNode->left;
pNode->left=pNode->right;
pNode-right=pTemp;
if(pNode->left)MirrorRecursively(pNode->left);
if(pNode-right)MirrorRecursively(pNode-right);
}
20、顺时针打印矩阵


21、包含min函数的栈
   2个栈,一个数据栈,一个辅助栈,辅助栈中栈顶始终数据栈的最小元素,若进栈元素大于最小值,则重复加


入 
   最小值到辅助栈中,因为它是当前最小,若小于最小值,则加入该最小值到辅助栈中(2个栈元素数量始终一致)


22、栈的压入弹出序列(判断第二个序列是否是第一个序列的弹出序列)
    如果第二个序列中当前要判断的元素刚好与栈顶元素相等,则直接pop出来,如果不等,则将第一个序列的


后面还没有入栈的元素入栈,直到将与之相等的元素入栈为止,如果第一个序列的所有的元素都入栈了,还没有


找到与之相等的元素,则说明第二个序列不是第一个序列的弹出序列


23、从上往下打印二叉树 队列,逐层遍历


24、二叉搜索树的后续遍历序列


25、二叉树中和为某一值的路径: 栈 先序遍历


26、复杂链表的复制:
    (1)A->A'->B->B'...(另一指针A->B)
    (2)关键是A'->other=A->other-next;
    (3)链表拆分 得到copy的链表
27、二叉搜索树与双向链表 (空)


28、字符串的排列:全排列


29、求数组中出现超过一半的数
    (1)基于partition函数的O(n)算法,同快排的partition。会改变数组中数字的位置
    (2)在遍历数组的时候,保存2个数字,一个是数组中的数,一个是次数,遍历到下一个数字时,如果和记


录的数字相等,次数+1;如果不同则次数-1;如果次数为0,则保存下一个数字,次数置为1。O(n)


30、最小的k个数
    (1)partition函数划分,O(n)
    (2)用最大堆作为装这k个数的容器(也可以用红黑树这种数据结构),每次取出最大的数,遍历完n个数后,得到最小的k个数。O(nlogk)
         (可以先判断k>n/2则求前n-k大)


31、求连续子数组的最大和  动态规划


32、从1到n整数中1出现的次数????


33、把数组排成最小的数(用字符串表示数字,解决大数问题??)


34、丑数?
(1)逐个判断该整数是不是丑数
(2)创建数组保存已经找到的丑数???


35、第一次只出现一次的字符:
   (1)扫描一遍字符串 int a[26]; a[字符-'a']++  hash的思想
    (2)再次扫描字符串 输出第一个满足 a[字符-'a']==1的字符


36、数组中的逆序对:在数组中的2个数字如果前面一个数字大于后面的数字,则这2个数字组成一个逆序对
    (1)暴力解法O(n^2){7 6 5}中有(7,6)(7,5)(6,5)
    (2)归并的思想??O(nlogn)


37、2个链表的第一个公共子节点
    最高效的解法:遍历链表得到长度m n 大的减小的=a,双指针 长的先走a步,再一起走,逐个判断节点    


             是否是同一个。O(m+n)


38、数字在排序数组中出现的顺序,二分法O(logn) 一般解法O(n)


39、求二叉树的深度  递归代码最简
    拓展:判断一棵树是否是平衡树(任一节点的左右子树的高度差不大于1)后序遍历每个节点只访问一次?


40、数组中只出现一次的数字(有2个这样的数):要求时间O(n) 空间O(1)
    (异或:相同数字异或为0,任一数异或0为本身)
    假若数组中只有一个数只出现一次,数组中数字逐个异或的结果就是只出现一次的数字;
    现在就要想办法,把这2个只出现一次的数分到2个子数组中,就能得到结果。
    4异或6=100异或110=010   差别就是在第二个bit位上,所以我们可以把所有数字按这个原则分成2组
    4和6一定会被分到不同的组中,相同的数字会被分到同一组中。


41、一个递增排序的数组和一个数s,在数组中查找2个数的和正好是s,输出任一对即可
    一般解法是暴力O(n^2)  双指针-首尾指针的解法是O(n)   


拓展:打印和为s的连续正数序列   15=1+2+3+4+5=4+5+6=7+8 打印(1 2 3 4 5)(4 5 6)(7 8 )
      先形成清晰的解题思路,才能开始写代码;借助上题双指针的思路
      最开始:small big 指向1 2   和为3<9 big增大 123 和为6<9 增大big 1234 10>9  增大small
      2 3 4 =9 打印 再增大big  2 3 4 5 和14>9 减小small。。。找到4 5 =9
     (循环退出条件:small<middle)其中middle=(1+sum)/2;
      
42、翻转字符串"I am a student."->"student. a am I" 
    2次翻转 先整个reverse再单个单词reverse
   
拓展:左旋字符串:输入abcde 2  输出cdeab
    先ab->ba   cde-> edc 即abcde->baedc
    baedc再reverse得到cdeab(注意边界 null)


43、n个色子的点数?


44、扑克牌的顺子?


45、圆圈中剩下的数字:
    题目:0 1 2 3 ...n-1这n个数字排成一圈,从0开始每次从这个圆圈中删除第m个数字,求这个圆圈最后剩


下哪个数字?
   有名的约瑟夫环问题


46、?求1+2+3+...+n要求不能乘除法、for while if else swith case 条件判断语句


47、不用加减乘除做加法


48、不能被继承的类  java中用final  C++中没有,可以把构造函数设为私有


49、字符串转换为整数(自己想好特殊的测试用例)


50、树中2个结点的最低公共祖先(问清楚需求,也许对方是故意漏掉条件考你的思路)
0 0