剑指offer题目解析简要总结

只简要总结最优解思路和一些核心代码

1 二维数组查找

描述:一个二维数组满足每一行从左到右递增,从上到下递增。给定这样数组和一个值,判断其是否在数组中。
思路:从数组右上方开始寻找,如果当前值等于目标值返回,如果当前值大于目标值则剔除该列,如果当前值小于目标值则剔除该行。

int row = 0;
int column = n - 1;
while(row < rows && column >=0){
	if(a[row * n + column] == target){
	find = true;
	break;
	}else if(a[row * n + column] > target){
		colunm --;
	}else{
		row ++;
	}
}

2 替换空格

描述:把字符串中的空格用 %20 替换
思路:先扫描一遍获得空格的个数,这样就可以计算出替换后的数组长度。然后从后往前进行字符串赋值和替换。

3 逆序打印链表的值

思路:(1)使用栈进行打印;
(2)链表翻转后打印;

4 重建二叉树

描述:根据前序和中序序列重建二叉树。
思路:现在前序序列中找到根节点,然后在中序序列中找根节点,中序序列中将根节点分为左右子树,递归构建。

5 用两个栈实现队列

思路:数字进入stack1,如果需要删除元素,将stack1中除了栈底元素外的其他元素弹出并放入第二个栈中,弹出stack1栈底元素,然后再把stack2中的元素弹出至stack1中。

6 求旋转数组最小值

思路:二分查找
如果数组中不存在重复数字,则根据mid值和左指针值比较大小即可。如果mid值大于左指针值则舍弃前半个区间,否则舍弃后半个区间。
如果存在重复数字,如果mid,左右指针的值都相等,那么需要顺序遍历左右指针中间的元素来找到最小值。

7 斐波那契数列

思路:(1)使用三个变量进行迭代;

long fib1 = 1;
long fib2 = 0;
long fibn = 0;
for(int i = 2; i <= n ;i ++){
	fibn = fib1 + fib2;
	fib2 = fib1;
	fib1 = fibn;
}
return fibn;

8 二进制中1的个数

思路:
(1)引入一个flag变量,每次向左移一位进行与运算;
(2)n = n & (n-1); 去掉最低位的1;

9 数值的整数次幂

思路:多次幂的方式。

double Pow(double base, int n){
if(n == 0) return 1;
if(n == 1) return base;
double result = Pow(base, n >>1);
result *= result;
if(n & 0x1 == 1) return result *= base;
return result;
}

10 打印数字

描述:例如给一个整数3 ,需要从1一直打印到最大的三位数999.
思路:(1)用字符串模拟打印过程;
(2)递归打印全排列;

11 O(1)复杂度删除一个节点

描述:给定一个链表和一个节点,删除该节点
思路:将需要删除节点的下一个节点内容复制到需要删除节点,然后删除下一个节点。注意考虑边界情况:删除节点是尾节点;链表只有一个节点;

12 调整数组顺序使得奇数位于偶数前面

思路:两个指针一个从前扫,另外一个从后扫。将判断条件单独写为一个函数以方便扩展。

13 链表倒数第K个节点

思路:两个指针,一个指针先走k-1步,然后连个指针一起走。注意:边界情况;

14 反转链表

注意:边界情况,时刻注意指针的指向;

15 合并两个有序链表

注意:链表的长度可能不同;

16 树的子结构

描述:输入两个二叉树,判断B是否是A的子结构(树的一部分,不同于子树)
思路:先找到根节点相同的节点,然后再递归查找

bool SubTree(root1, root2){
	bool result = false;
	if(root1 != null && root2 != null){
	if(root1.val == root2.val){
		result = judge(root1, root2);
	}
	if(!result){
		result = SubTree(root1.left, root2);
	}
	if(!result){
		result = SubTree(root1.right, root2);
	}
}
return result;
}

bool judge(root1, root2){
	if(root2 == null) return true;
	if(root1 == null) return false;
	if(root1.val != root2.val) return false;
	return judge(root1.left,root2.left) && judge(root1.right, root2.right);
}

17 二叉树的镜像

描述:输入一个二叉树,输出其镜像
思路:左右相互交换

void Mirror(root){
	TreeNode tmp = root.left;
	root.left = root.right;
	root.right = tmp;
	if(root.left){
		Mirror(root.left);
	}
	if(root.right){
		Mirror(root.right);
    }
}

18 顺时针打印矩阵

思路:找规律按照索引进行四个方向的打印即可

19 实现包含min函数的栈

思路:利用一个辅助栈,每次压入元素时都将最小值放入辅助栈,每次弹出值都将辅助栈的栈顶元素弹出。

if(min.size() == 0 || value < min.top())
	min.push(value);
else
	min.push(min.top());

20 栈的压入、弹出序列

思路:模拟。如果下一个弹出数字是栈顶数字直接弹出,如果不是栈顶元素,把压栈序列中还没有入栈的数字压入辅助栈,直到遇到需要弹出的数字位置。如果所有数字都压入栈了仍然没有找到下一个弹出的数字则不是弹出序列。

21 树的层次遍历

思路:使用队列

while(queue.size()){
	TreeNode node = queue.pop();
	print(node);
	if(node.left) queue.offer(node.left);
	if(node.right) queue.offer(node.right);
}

22 给定一个序列判断是否是二叉搜索树的后序遍历序列

思路:找到左右子树结构,然后分别进行判断;

23 二叉树中和为某个值的路径

思路:回溯法(扫描节点,期望值,路径,当前值)
终止条件:和为目标值且是叶子节点;
往下遍历:如果左右孩子不为空则递归寻找;
回溯:路径中删除当前节点,当前值中减去当前节点的值;

24 复杂链表复制

25 字符串全排列

思路:回溯(结果串,需要打印的开始位置)

26 数组中出现次数超过一半的数字

思路:(1)基于快排求第 n/2 大数;
(2)摩尔计数法;两个变量,一个存储数字,一个存储出现的次数。如果下一个数字等于存储的数字则次数加一,如果不等于次数减一。每一轮循环都要判断次数,如果次数为0,更新保存的数字并把次数置为1;

int result = a[0];
int times = 1;
for(int i = 1; i < length ;i ++){
	if(times == 0){
		result = a[i];
		times = 1;
}else if (a[i] == result){
	times ++;
}else{
	times --;
}
}
return result;

27 求前K小数

思路:(1)基于快排思想
(2)构建一个K大小的堆

28 连续子数组最大和

思路:(1)一直累和,如果和小于等于0则令和等于当前值,并更新最大值;

int sum = 0;
int max = 0;
for(int i = 0; i < length ; i ++){
	if(sum <= 0) sum = a[i];
	else sum += a[i];
	if(sum > max) max = sum;
}
return max;

(2)动态规划。
状态定义:dp[i]表示以第i个数字结尾的最大和
转移方程: i==0 || dp[i-1]<0 dp[i] = a[i];
dp[i-1] > 0 dp[i]=dp[i-1]+a[i];

29 1到n中 1 出现的个数

思路:寻找规律利用递归

30 数组排成最小的数

思路:改写大于,小于和等于的形式进行排序

31 丑数

思路:(1)逐个判断是否是丑数;
(2)用数组保存已有的丑数并不断生成;

32 第一个只出现一次的字符

思路:第一遍使用哈希表进行计数;第二遍寻找第一个出现的字符;

33 求数组逆序对

思路:参考归并排序

34 两个链表的第一个公共节点

思路:(1)使用hashset将第一个链表所有节点放入,然后搜索第二链表;
(2)采用两个辅助栈;
(3)先遍历两个链表得到他们的长度然后再比较;
(4)两边遍历,到头之后再从另外一个链表头遍历;

35 数字在排序数组中出现的次数

思路:(1)二分查找找第一次出现的位置和最后一次出现的位置;

36 二叉树的深度

思路:递归判断

int depth(root){
if(root == null) return 0;
int left = depth(root.left);
int right = depth(root.right);
return left > right ? left + 1 : right + 1;
}

37 数组中只出现一次的数字

思路:异或运算;

38 两数和

思路:双指针
如果求某个序列等于目标值,双指针,如果连个指针之间的和大于目标值则增大左指针,如果小于目标值则增大右指针,相等之后打印并增大右指针。

39 翻转单词顺序

思路:先翻转整个句子,然后再翻转每个单词;
旋转一个字符串,两边翻转。

40 n个骰子的点数

思路:1–6的排列组合问题

41 扑克牌的顺序

思路:采用数字进行模拟

42 约瑟夫环

描述:0,n-1个数排成一个环,每次删除第m个数字。
思路:(1)用环形链表模拟;
(2)巧妙的方法

int last(int n , int m){
	if(n < 1 || m < 1)  return -1;
	int last = 0;
	for(int i = 2; i <= n ;i ++){
		last = (last+m)%i;
	}
return last;
}

43 位运算实现两数和

int add (int a , int b){
	int sum;
	int carry;
	do{
		sum = a ^b;
		carry = (a&b) <<1;
		a = sum;
		b = carry;
}while(b != 0);
return a;
}

44 树中两个节点最近公共祖先

思路:(1)二叉搜索树,判断根节点值的大小进行;
(2)有父指针相当于求交;
(3)找到根节点到两个节点的路径,求两个链表的最后公共节点;

你可能感兴趣的:(LeetCode,数据结构与算法)