剑指offer刷题记录(一)

我的解法如下: 

class Solution {
    public String reverseLeftWords(String s, int n) {
        Solution solution = new Solution();
        char value;
        char [] stringArr = s.toCharArray();//首先将String类型的s转变为char数组
        int length = stringArr.length;
        for (int i = 0; i <n ; i++) {
            value = stringArr[0];
            stringArr[0] = 0;
            solution.move(stringArr);
            stringArr[length-1] = value;
        }
        String out = String.valueOf(stringArr);
        return out;

    }
    public void move(char[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            if (arr[i + 1] != 0) {
                arr[i] = arr[i + 1];
            }else{
                arr[i] = 0;
                break;
            }
        }

    }
       
}

问题:速度慢,繁琐,原始

这里总结我这道题时出现的问题或者细节:

1.char和string的区别:

1. char是表示的是字符,定义的时候用单引号,只能存储一个字符。例如; char='d'.  而String表示的是字符串,定义的时候用双引号,可以存储一个或者多个字符。例如:String=“we  are neuer”。

2. char是基本数据类型,而String是个类,属于引用数据类型。String类可以调用方法,具有面向对象的特征。

2.在静态的方法中不能直接调用非静态的方法或属性

解决办法:

1. 将被调用的方法设置成静态方法;
2. new本类,然后通过实例来调用。

3.将字符串和字符串数组互相转换方法

java可以使用两种方法直接将字符数组转为字符串
方法1:直接在构造String时转换。
char[] data = {‘a’, ‘b’, ‘c’};
String str = new String(data);
方法2:调用String类的方法转换。
String.valueOf(char[] ch)

java可以使用两种方法直接将字符串转为字符数组
情况一:如果是有分隔符的那种例如”abc,def,ghi”;就直接分割就行了.
String string = “abc,def,ghi”;
String [] strArr= string.split(“,”); //注意分隔符是需要转译
情况二:如果是”abcdefghijk”这种字符串,就直接
String string1 = “abcdefghijk” ;
char [] strArr1 = string1.toCharArray(); //注意返回值是char数组
 

我见到最简单解法如下:

class Solution {
    public String reverseLeftWords(String s, int n) {
        String str1=s.substring(0,n);
        String str2=s.substring(n);
        StringBuilder sb=new StringBuilder();
        sb.append(str2).append(str1);
        return sb.toString();
    }
}

1.substring() 方法返回字符串的子字符串。 简单说就是切割字符串

public String substring(int beginIndex) 或 public String substring(int beginIndex, int endIndex)

begin是包括,end不包括

2.StringBuilder类和StringBuffer类非常像,前者速度快,应用广,但是后者在考虑线程安全的时候使用。

转载一个别人总结的:https://blog.csdn.net/x83853684/article/details/82081658

 

2.

我的解法:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.lang.Math;

class Solution {
    public int[] printNumbers(int n) {
        List<Integer> list=new ArrayList<Integer>();
        for (int i = 1; i < Math.pow(10,n); i++) {
            list.add(i);
        }
        int[] outer = new int[list.size()];
        for (int i = 0; i <list.size() ; i++) {
            outer[i] = list.get(i);
        }
        return outer;
}
}

我这个可以优化的地方:普通数组是无法在创建之后,更改长度的,所以我选用了ArrayList,但是为什么我们不能在获取n之后就计算出数组应该存入多少数据呢?

怎么样去避免使用Math.pow这个函数呢,下面的解法在时间大幅度提升。

class Solution {
    public int[] printNumbers(int n) {
        int sum = 9;
        while(n > 1){
            sum = sum * 10 + 9;
            n--;
        }
        int[] arr = new int[sum];
        for(; sum > 0;){
            arr[sum - 1] = sum--;
        }
        return arr;
    }
}

 

3.

这里直接上题解:使用快慢指针

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode slow = head, fast = head;
        for (int i = 0; i < k; i++)
            fast = fast.next;
        
        while (fast != null) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

让快指针先走K步,再让他们俩同时走,这样我们就能保证快指针拥有领先慢指针K步,所以当快指针走完时,慢指针就指向倒数第K个指针。

转载一个快慢节点的应用:https://www.jianshu.com/p/21b4b8d7d31b

 

4.

总体难度不大,我的解法有点投机:

class Solution {
    public String replaceSpace(String s) {        
        return s.replace(" ", "%20");
    }
    
}

直接调用JAVA lang包下的replace方法,将空格替换成“%20”

看答案还有一种复杂一点:

 public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
		for(int i = 0;i<s.length();i++) {
			if(s.charAt(i) == ' ') {
				sb.append("%20");
			}else {
				sb.append(s.charAt(i));
			}
		 }
	     return sb.toString();

这里补充一个打印数组的知识点:

三种打印数组的方法:1.for循环遍历 2.增强for循环 3.调用Array.tostring()方法

https://blog.csdn.net/chenkaibsw/article/details/78989459

 

5.

答案一:

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode node = helper(root);
        return node;
    }
    public TreeNode helper(TreeNode root){
        if(root == null){
            return null;
        }
        TreeNode mid = root.left;//中转来储存你的左节点
        root.left = helper(root.right);
        root.right = helper(mid);
        return root;
    }
}

这个非常善用递归,在对树(指针实现)进行编程的时候,因为层层往下的结构都是一样的,所以递归在里面就非常地好用。

答案二是参考一个大佬,分别前中后和层次遍历来解答这题,不使用递归。

核心代码:

TreeNode temp = node.left;
node.left = node.right;
node.right = temp;

前序遍历非递归

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if (root == null){
            return null;
        }
        TreeNode node = root;
        Deque<TreeNode> stack = new LinkedList<>();
        while (node != null || !stack.isEmpty()){
            if (node != null){
                // 交换左右子树
                inventTreeNode(node);
                // 原先的先序遍历
                stack.push(node);
                node = node.left;
            }else {
                node = stack.pop();
                node = node.right;
            }
        }
        return root;
    }
    
    private void inventTreeNode(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

这里的Deque是双向队列,可以直接实现Stack和Queue两种数据结构的功能。具体看下面这个网址:

https://blog.csdn.net/u013967628/article/details/85210036

中序遍历非递归

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if (root == null){
            return null;
        }
        TreeNode node = root;
        Deque<TreeNode> stack = new LinkedList<>();
        while (node != null || !stack.isEmpty()){
            if (node != null){
                stack.push(node);
                node = node.left;
            }else {
                node = stack.pop();
                // 交换左右子树
                inventTreeNode(node);
                // 这里因为交换了,不能继续往右子树走,因为现在的右子树,是原来的左子树
                // 所以接下来要走左边
                node = node.left;
            }
        }
        return root;
    }
    
    private void inventTreeNode(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

后序遍历非递归

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if (root == null){
            return null;
        }
        TreeNode node = root;
        TreeNode tempNode = null;// 记录上次访问的节点
        Deque<TreeNode> stack = new LinkedList<>();
        while (node != null || !stack.isEmpty()){
            if (node != null){
                stack.push(node);
                node = node.left;
            } else {
                node = stack.peek();
                if (node.right == null || node.right == tempNode){
                    // 右节点为空,或者右节点已经被访问过
                    // 当前节点出栈
                    tempNode = stack.pop();
                    // 交换
                    inventTreeNode(node);
                    // 这里要置空,不然node还是存在,又会往左子树去了
                    node = null;
                }else {
                    node = node.right;
                }
            }
        }
        return root;
    }
    
    private void inventTreeNode(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

层次遍历非递归

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode node = root;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(node);
        while(!queue.isEmpty()){
            node = queue.poll();
            // 翻转左右子树
            inventTreeNode(node);
            if(node.left != null){
                queue.offer(node.left);
            }
            if(node.right != null){
                queue.offer(node.right);
            }
        }
        return root;
    }
    
    private void inventTreeNode(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

 

6.

方法一:使用双指针来解决这个问题。解题思路看下图:

申请两个指针,第一个指针叫 pre,最初是指向 null 的。
第二个指针 cur 指向 head,然后不断遍历 cur。
每次迭代到 cur,都将 cur 的 next 指向 pre,然后 pre 和 cur 前进一位。
都迭代完了(cur 变成 null 了),pre 就是最后一个节点了。

代码如下:

class Solution {
	public ListNode reverseList(ListNode head) {
		//申请节点,pre和 cur,pre指向null
		ListNode pre = null;
		ListNode cur = head;
		ListNode tmp = null;
		while(cur!=null) {
			//记录当前节点的下一个节点
			tmp = cur.next;
			//然后将当前节点指向pre
			cur.next = pre;
			//pre和cur节点都前进一位
			pre = cur;
			cur = tmp;
		}
		return pre;
	}
}

方法二:使用递归,递归理解确实非常困难

三个问题:1.这个函数是用来解决什么的 2.递归调用的结束条件是什么 3.寻求一个等式关系

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
	public ListNode reverseList(ListNode head) {
		//递归终止条件是当前为空,或者下一个节点为空
		if(head==null || head.next==null) {
			return head;
		}
		//这里的cur就是最后一个节点
		ListNode cur = reverseList(head.next);
		//这里请配合动画演示理解
		//如果链表是 1->2->3->4->5,那么此时的cur就是5
		//而head是4,head的下一个是5,下下一个是空
		//所以head.next.next 就是5->4
		head.next.next = head;
		//防止链表循环,需要将head.next设置为空
		head.next = null;
		//每层递归函数都返回cur,也就是最后一个节点
		return cur;
	}
}

7.

我的解法:2 ms

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        Deque<Integer> stack = new LinkedList<>();
        while(head != null){
            stack.push(head.val);
            head = head.next;
        }
        int [] outer = new int[stack.size()];
        int i =0;
        while(!stack.isEmpty()){
            outer[i++] = stack.pop();
        }
        return outer;

    }
}

我这个方法简单明了,先弄一个栈,利用栈先入后出的特点,将一个一个节点的数值压栈,然后再将栈里的值一一出栈就是我们所需要的数组。但这里需要两个循环,因为首先要入栈然后再出栈。

 

方法二:一个大神的答案,使用的递归   1ms

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    ArrayList <Integer> arrays = new ArrayList<>();
    public int[] reversePrint(ListNode head) {
        //递归的结束条件:head == null       
        recur(head);
        int [] outer = new int[arrays.size()];
        for(int i=0;i<outer.length;i++){
            outer[i] = arrays.get(i);
        }
        return outer;
    }
    public void recur(ListNode head){
        if(head == null){
            return;
        }
        recur(head.next);
        arrays.add(head.val);
    }
}

将head.next编程递推函数的参数,定位到链表最后一位。所以这里arrays.add(head.val)添加顺序并不是从头到尾,而是从尾到头。添加完毕后只需要将其输出就行。

这里当时还发生了一个错误,我没有将ArrayList <Integer> arrays = new ArrayList<>();这一行写到reversePrint方法外,想想为什么不这么做就报错?报错行是arrays.add(head.val);

 

8.

做了半天,没做出来。贴出别人的解法

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dum = new ListNode(0), cur = dum;
        while(l1 !=null &&l2 !=null){
            if(l1.val >= l2.val){
                cur.next = l2;
                l2=l2.next;
                cur = cur.next;
                
            }else{
                cur.next = l1;
                l1=l1.next;
                cur = cur.next;
            }
        }
        if(l1 !=null){
            cur.next = l1;

        }else{
            cur.next = l2;
        }
        return dum.next;
    }
}

看了大佬思路,我完成的代码,思路如下:

这里我有2个问题,值得以后反复琢磨:

1.为什么return dum.next;就可以把整条链条都给返回?

2.伪头结点的意义?

 

9.

class CQueue {
    Deque<Integer> stack1,stack2;
    public CQueue() {
        stack1 = new LinkedList<Integer>();
        stack2 = new LinkedList<Integer>();

    }
    
    public void appendTail(int value) {        
             stack1.push(value);
}
    
    public int deleteHead() {
        if(!stack2.isEmpty()){
            return stack2.pop();
        }else if (!stack1.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }else{
            return -1;
        }
    }
}

解题思路:简而言之就是stack2作为改变stack1顺序的一个工具,因为比如输入123,stack1里面就是321,这时候输出肯定是不对的,这就不是队列了,所以需要Stack2来倒序,继续变成123,这时候再输出就行。

10.

解法一:主要思路就是用位运算,n&1,相当于n的最右边一位和1做位运算,当然如果最右边一位是1,得到1反之则为0 .

然后需要将n右移,Java中有两个右移“>>”(有符号)和“>>>”(无符号)两种。

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int count = 0;
        while(n != 0){
            int tmp = n & 1;
            if(tmp  == 1){
                count++;
            }
            n >>>= 1;
        }
        return count;
    }
}

解法二:一个大佬的思路非常巧妙

public class Solution {
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            res++;
            n &= n - 1;
        }
        return res;
    }
}

思路如下:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值