剑指offer学习笔记
JZ3
题目描述:数组中重复的数字
解决的思路:hashset(不可以输入重复的数据);重新创建一个数组判断。
代码:
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
//hashset判断
public int duplicate (int[] numbers) {
// write code here
HashSet<Integer> set = new HashSet<>();
for (int i = 0;i<numbers.length;i++){
if(set.add(numbers[i]))
return numbers[i];
}
return -1;
}
//数组判断
public int duplicate (int[] numbers){
int[] countarray = new int[numbers.length];
for(int i=0;i<numbers.length;i++){
countarray[numbers[i]]++;
if(countarray[numbers[i]]>1){
return numbers[i];
}
}
return -1;
}
}
JZ4
题目描述:二维数组中的查找
解决的思路:二分查找(分治)
step 1:首先获取矩阵的两个边长,判断特殊情况。
step 2:首先以左下角为起点,若是它小于目标元素,则往右移动去找大的,若是他大于目标元素,则往上移动去找小的。
step 3:若是移动到了矩阵边界也没找到,说明矩阵中不存在目标值。
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param target int整型
* @param array int整型二维数组
* @return bool布尔型
*/
public boolean Find (int target, int[][] array) {
// write code here
if(array.length == 0)
return false;
int n = array.length;
if(array[0].length == 0)
return false;
int m = array[0].length;
for(int i = n-1,j = 0; i>=0&&j<m;){
if(array[i][j]>target)
i--;
else if(array[i][j]<target)
j++;
else
return true;
}
return false;
}
}
JZ5 替换空格
解题思路:申请一个临时数组,然后再遍历这个字符串的每个字符,如果不是空格就把遍历的字符添加到临时数组中,如果是空格就添加3个字符’%',‘2’,'0’分别到临时数组中,最后再把临时数组转化为字符串即可。
public String replaceSpace (String s) {
// write code here
//先把字符串转换成字符,再转换成字符串
int length = s.length();
char [] array = new char[length * 3];
int index = 0;
for(int i=0;i < length;i++){
char c = s.charAt(i);
if(c == ' '){
array[index++]='%';
array[index++]='2';
array[index++]='0';
}else{
array[index++]= c;
}
}
String newstr = new String(array,0,index);
return newstr;
}
StringBuilder方法;好处空间O(1)
public String replaceSpace (String s) {
StringBuilder stringbuilder = new StringBuilder();
for(int i = 0;i<s.length();i++){
if(s.charAt(i)==' '){
stringbuilder.append("%20");
}else{
stringbuilder.append(s.charAt(i));
}
}
return stringbuilder.toString();
}
JZ6 从尾到头打印链表
解题思路:递归方法
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;
}
}
非递归:
ArrayList 中有个方法是 add(index,value),可以指定 index 位置插入 value 值
所以我们在遍历 listNode 的同时将每个遇到的值插入到 list 的 0 位置,最后输出 listNode 即可得到逆序链表
public class Solution {
ArrayList<Integer> list = new ArrayList<>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ListNode tmp = listNode;
while(tmp != null){
list.add(0,tmp.val);
tmp = tmp.next;
}
return list;
}
}
JZ9 用两个栈实现队列
队列的特性是:“先入先出”,栈的特性是:“先入后出”
当我们向模拟的队列插入数 a,b,c 时,假设插入的是 stack1,此时的栈情况为:
栈 stack1:{a,b,c}
栈 stack2:{}
当需要弹出一个数,根据队列的"先进先出"原则,a 先进入,则 a 应该先弹出。但是此时 a 在 stack1 的最下面,将 stack1 中全部元素逐个弹出压入 stack2,现在可以正确的从 stack2 中弹出 a,此时的栈情况为:
栈 stack1:{}
栈 stack2:{c,b}
继续弹出一个数,b 比 c 先进入"队列",b 弹出,注意此时 b 在 stack2 的栈顶,可直接弹出,此时的栈情况为:
栈 stack1:{}
栈 stack2:{c}
此时向模拟队列插入一个数 d,还是插入 stack1,此时的栈情况为:
栈 stack1:{d}
栈 stack2:{c}
弹出一个数,c 比 d 先进入,c 弹出,注意此时 c 在 stack2 的栈顶,可直接弹出,此时的栈情况为:
栈 stack1:{d}
栈 stack2:{c}
根据上述栗子可得出结论:
当插入时,直接插入 stack1
当弹出时,当 stack2 不为空,弹出 stack2 栈顶元素,如果 stack2 为空,将 stack1 中的全部数逐个出栈入栈 stack2,再弹出 stack2 栈顶元素
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.size() <= 0){
while(stack1.size() != 0){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
JZ10 斐波那契数列
思路:递归法
if(n<=2){
return 1;
}
return Fibonacci(n-1)+Fibonacci(n-2);
动态规划五步,1先确定dp[ ],dp[i],2确定递推公式,dp[i]=dp[i-1]+dp[i-2],3初始化dp数组,dp[0]=0,dp[1]=1;,4确定遍历顺序,5举例推到dp数组
if(n<=1){
return n;
}
int dp[] = new int[n+1];
dp[0]=0;
dp[1]=1;
for(int i =2;i<=n;i++){
dp[i] = dp[i-1]+dp[i-2];
}
return dp[n];
优化上面算法,观察可以发现我们不用每次都记录整个dp数组,只需要记录两个数字就行
if(n<=1){
return n;
}
int dp[] = new int[n+1];
dp[0]=0;
dp[1]=1;
for(int i =2;i<=n;i++){
int sum = dp[0]+dp[1];
dp[0]=dp[1];
dp[1]=sum;
}
return dp[1];