1.二维数组中的查找
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
1.见到数组有序便应条件反射折半查找:一维二分查找、二维二分查找,(这个的边界条件把自己改晕了);
public class Solution {
public boolean Find(int target, int [][] array) {
if((array==null||array.length==0)||(array.length==1&&array[0].length==0))
return false;
else
return divide(target,0,0,array.length-1,array.length-1,array);
}
public boolean divide(int target, int i, int j, int i_end, int j_end,int [][] array) {
int mid_i=(i+i_end)/2,mid_j=(j+j_end)/2;
if(i==i_end&&j==j_end)
if(array[i][j]==target)
return true;
else
return false;
if(array[mid_i][mid_j]==target)
return true;
else if(array[mid_i][mid_j]>target&&i<=mid_i&&j<=mid_j&&mid_i<=i_end&&mid_j<=j_end)
return divide(target,i,j,mid_i,mid_j,array)||divide(target,mid_i+1,j,i_end,mid_j,array)||divide(target,i,mid_j+1,mid_i,j_end,array);
else if(i<=mid_i&&j<=mid_j&&mid_i<=i_end&&mid_j<=j_end)
return divide(target,mid_i+1,mid_j+1,i_end,j_end,array)||divide(target,mid_i+1,j,i_end,mid_j,array)||divide(target,i,mid_j+1,mid_i,j_end,array);
return false;
}
}
2.从左下(右上)查找:用某行最小或某列最大与 target 比较,每次可剔除一整行或一整列。
2.替换空格
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
1.replace()函数;
2.遍历一遍记录空格个数,从后往前替换(重复移动多次时可考虑从后向前遍历)。
3.从头到尾打印链表
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
1.add(index,value);
2.递归实现;
3.反转链表(需要三个额外指针)。
4.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
1.递归。
5.两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
1.一个进一个出。
6.旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
1.在两段范围内都是非降序,当不符合这个规律时,就找到了最小数字;
2.有序条件反射二分查找!!!!如果没有目标值,一般可以考虑 端点。旋转数组有个特点:数组首元素若大于最后一个元素,则为旋转数组,否则为正常数组。
7.斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
1.递归;
2.动态规划。
public class Solution {
public int Fibonacci(int n) {
int a = 0;
int b = 1;
for(int i=1;i<=n;i++){
b = a + b;
a = b - a;
}
return a;
}
}
8.跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
1.跟7一样一样的…
9.变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
1.动规:f(n)=f(n−1)+f(n−2)+……f(0) 累加(左移乘2,右移除2 ;x&0x1=1是奇数);
2.f(n)=2f(n−1)。
10.矩形覆盖
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
1.递归,类似于789,需考虑重复情况。
11.二进制中1个数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
1.这个题自己是先转二进制,然后求补码,实现的有些麻烦,事实上java本来存的就是补码,直接用二进制计算就行:把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作(很多二进制都可参考此思路)。
public class Solution {
public int NumberOf1(int n) {
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
}
2.每次判断最低位是否为1
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while(n != 0){
count += (n & 1);
n >>>= 1;
}
return count;
}
}
12.数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0
1.快速幂算法,注:这种类型题注意溢出(pow函数就是这么实现的);
class Solution {
public:
double Power(double b, int n) {
if (n < 0) {
b = 1 / b;
n = -n;
}
double x = b; // 记录x^0, x^1, x^2 ...
double ret = 1.0;
while (n) {
if (n&1) {
ret *= x; // 二进制位数是1的,乘进答案。
}
x *= x;
n >>= 1;
}
return ret;
}
};
2.暴力法。
13.调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
1.i++往前走碰到偶数停下来;j++前进,直到碰到奇数;j经过的j-i个偶数依次后移,a[j]对应的奇数插到a[i]位置
public class Solution {
public void reOrderArray(int [] array) {
for (int i = 0; i < array.length - 1; i++) {
if (array[i] % 2 == 0) {
int j = i;
while (array[++j] % 2 == 0) {
if (j == array.length - 1) {
i=j;
break;
}
}
int t = array[j];
while (j > i) {
array[j] = array[j - 1];//数组后移
j--;
}
array[j] = t;
}
}
}
}
优化:用一个变量记住上次的偶数序号,可将内层两个循环变为一个;
2.借鉴冒泡排序的特点,让偶数不断向右边移动。
14.链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点(求链表的中间节点:快慢指针)ps:有的时候一个指针不能接解决需考虑两个指针,不同速度。
1.快慢指针;
2.转成list;
3.两次遍历。
15.反转链表
输入一个链表,反转链表后,输出新链表的表头。
1.头插法,非递归(需三个指针):
public class Solution {
public ListNode ReverseList(ListNode head) {
ListNode p = head;
ListNode q = null;
ListNode t = null;
while (p != null) {
q = p;
p = p.next;
q.next = t;
t = q;
}
return q;
}
}
递归(todo)。
16.合并两个排序的列表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
1.归并排序,非递归:
2.递归。
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if (!pHead1) return pHead2;
if (!pHead2) return pHead1;
if (pHead1->val <= pHead2->val) {
pHead1->next = Merge(pHead1->next, pHead2);
return pHead1;
}
else {
pHead2->next = Merge(pHead1, pHead2->next);
return pHead2;
}
}
};
17.树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
1.再写一个判断两个树相同的函数,调用,需要注意的是这个函数不是判断两个树完全相同,只是从根节点出发root1包括root2就可,边界调件和判断两个树完全相同有区别。
bool dfs(TreeNode *r1, TreeNode *r2) {
if (!r2) return true;
if (!r1) return false;
return r1->val==r2->val && dfs(r1->left, r2->left) && dfs(r1->right, r2->right);
}
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if (!pro1 || !pRoot2) return false;
return dfs(pRoot1, pRoot2) || HasSubtree(pRoot1->left, pRoot2) ||
HasSubtree(pRoot1->right, pRoot2);
}
18.二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
1.深度遍历;递归:
class Solution {
public:
TreeNode* dfs(TreeNode *r) {
if (!r) return nullptr;
TreeNode *lval = dfs(r->left);
TreeNode *rval = dfs(r->right);
r->left = rval, r->right = lval;
return r;
}
void Mirror(TreeNode *pRoot) {
if (!pRoot) return;
dfs(pRoot);
}
};
2.广度遍历。
class Solution {
public:
void Mirror(TreeNode *pRoot) {
queue<TreeNode*> pq;
pq.push(pRoot);
while (!pq.empty()) {
int sz = pq.size();
while (sz--) {
TreeNode *node = pq.front();
pq.pop();
if (node->left) pq.push(node->left);
if (node->right) pq.push(node->right);
// our tasks
TreeNode *cur = node->left; // 需要保存一下
node->left = node->right;
node->right = cur;
} // end inner while
} // end outer while
}
};
19.顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
1.如果矩阵是n×n,代码会简单,这个题矩阵是n×m(m.n不一定相等),定义四个变量代表范围,up、down、left、right,逐渐向内逼近;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> result = new ArrayList<>();
if(matrix == null)return result;
int low = 0;
int high = matrix.length-1;
int left = 0;
int right = matrix[0].length-1;
while(low <= high && left <= right){
//向右
for(int i=left; i <= right; i++)
result.add(matrix[low][i]);
//向下
for(int i = low+1; i <= high; i++)
result.add(matrix[i][right]);
//向左 有可能出现特殊的情况只有一行,为了避免重复访问
if(low < high){
for(int i= right-1; i >= left; i--)
result.add(matrix[high][i]);
}
//向上 有可能出现特殊的情况只有一列,为了避免重复访问
if(left < right){
for(int i = high-1; i >= low+1; i--)
result.add(matrix[i][left]);
}
low++;
high--;
left++;
right--;
}
return result;
}
2.也可先吸收第一行,并将第一行从矩阵中去掉;然后将矩阵“变相转置”(这里的转置可以理解为将矩阵从地上立起来,比如矩阵是【【1,2,3】, 【4,5,6】】 将它“变相转置”(立起来)为 【【3,6】,【2,5】,【1,4】】 );重复以上两步。
20.包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
1.双栈;
2.以第一个入栈的元素为基准,存储差值:每个元素在数值上都包含了min值,举个例子,假设入栈序列为:4、5、6、3、2、1,那么各轮次对应的min值就是:4、4、4、3、2、1,发现有:4=4+0,5=4+1,6=4+2,3=4+(-1),2=3+(-1),1=2+(-1);各个元素在数值上已经包含了在它之前的最小值的值;那么,我们只要在数据栈中存储0、1、2、-1、-1、-1,然后再使用一个辅助变量min=1就可以了。
public class Solution {
private static Stack<Integer> stack = new Stack<Integer>();
private int min;
public void push(int node) {
if (stack.empty()){
min=node;
}
stack.push(node-min);
if (node<min){
min=node;
}
}
public void pop() {
int x=stack.pop();
if (x<0){
min=min-x;
}
}
public int top() {
int x=stack.peek();
if(x<0){
return min;
}
return min+x;
}
public int min() {
return min;
}
}