剑指offer
一.
Java查找二维数组时,如何确定二维数组的行列长度
确定二维数组行数:int rowLength = array.length;
确定二维数组列数:int colLength = array[0].length;
二。
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
解法一:
调用自带函数replaceAll()
解法二:
用新的StringBuffer存储,每当扫描到空格时就append("%20") ,否则扫到什么append什么
三.
List容器的下标从0开始
List ls = new ArrayList();
ls.add("first");
System.out.println(ls.get(0));
非递归思路:ArrayList 中有个方法是 add(index,value),可以指定 index 位置插入 value 值
所以我们在遍历 listNode 的同时将每个遇到的值插入到 list 的 0 位置,最后输出 listNode 即可得到逆序链表
递归代码:
public class Solution { ArrayList<Integer> list = new ArrayList();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode!=null){
printListFromTailToHead(listNode.next);
list.add(listNode.val);
}
return list;
}
}
四.旋转数组的最小数字
初始想法是顺序查找直到出现后一个数比前一个数字小的情况
看了题解后,可以采用时间复杂度更低的二分查找
五.斐波那契数列
递归解法并不难,重点是在存储和递归的优化需要好好研究一下。
https://www.nowcoder.com/questionTerminal/c6c7742f5ba7442aada113136ddea0c3?answerType=1&f=discussion
六.跳台阶
变形的斐波那契数列
dp(i) = dp(i-1)+dp(i-2)
七.变态跳台阶
变形的斐波那契数列
f(n) = f(n-1)+f(n-2)+f(n-3)+…
f(n-1) = f(n-2)+f(n-3)+…
上式-下式可得:f(n) = f(n-1)
斐波那契数列的变形掌握不够熟练!
八.矩阵覆盖
每次可以横向摆放覆盖一行剩余n-1行
也可以竖向摆放覆盖两行剩余n-2行
f(n) = f(n-1)+f(n-2)
九.二进制中1的个数
原码:第一位代表符号位。
反码:在原码的基础上,除符号位外,逐位取反。
补码:反码+1
直观思想:将输入数字与0xffffffff相与,再进行1的计数。
正常思路:将数字右移再与1相与,但负数右移会再右侧补1,因此可采用将1左移与数字相与的策略,直到将1移出计数范围变成0。
更好的思路:将数字-1后再与原数字做"&"运算,每次将原数字-1都会将原数字最右侧的1置为0,并且将该数字右侧的所有0转换为1,然后再将该转换后的数字与原数字相与,就相当于每次只把原数字最右侧的一个1置为0,于是原数字有多少个1就可以进行多少次这样的运算。
public int NumberOf1(int n) {
int count = 0;
while (n != 0) {
count++;
n = n & (n-1);
}
return count;
}
十.数值的整数次方
直观思路:直接采用循环的方式进行计算,需要考虑指数为负数的情况,需要将结果取倒数
时间复杂度O(n)。
递归想法:n为偶数,an=an/2 × an/2;
n为奇数an=(a(n-1)/2)*(a(n-1)/2)*a
时间复杂度O(logn)
十一.反转链表
思路:用三个节点分别存储当前头部的前一个节点,当前头部,头部的后一个节点。每次将当前头部的指针指向前部,在将头部的节点移动到当前头部,之前的后部节点变为head节点
public ListNode ReverseList(ListNode head) {
ListNode pre = null;
ListNode next = null;
//pre->head->next
//pre<-head next
//temp<-pre head->next
while (head != null) {
next = head.next;
head.next = pre;
//pre move to the next node
pre = head;
head = next;
}
return pre;
}
十二.合并两个排序的链表
非常经典的一道题!
递归解法:
想法较为简单,首先判断当前两个链表是否为空,若一个为空则直接返回另一个,再判断两链表头节点数值的大小,若 list1 > list2 则将list2.next和list1继续合并,并将合并后的结果链接到list2上
public ListNode Merge(ListNode list1,ListNode list2) {
if (list1 == null) return list2;
else if (list2 == null) return list1;
if (list1.val > list2.val) {
list2.next = Merge(list2.next,list1);
return list2;
}
else {
list1.next = Merge(list1.next,list2);
return list1;
}
}
非递归解法:
多次出错的原因: 搞清楚定义头节点时应该将哪个部分进行初始化,应该将初始化空间的节点作为程序中进行操作的节点,后指向该节点的node作为真正的返回值,在结果处返回。
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode p = new ListNode(0);//就是这个地方!
ListNode ans = p;
while (list1 != null && list2 != null) {
if (list1.val < list2.val) {
p.next = list1;
p = p.next;
list1 = list1.next;
}
else {
p.next = list2;
p = p.next;
list2 = list2.next;
}
}
if (list1 != null) p.next = list1;
if (list2 != null) p.next = list2;
return ans.next;
}