剑指offer第三章刷题

前言:代码质量包括规范性(布局清晰,命名合理,手写能认出来),代码的完整性(功能测试,边界测试,负面测试)

括号匹配

在这里插入图片描述

class Solution {
    public boolean isValid(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        Stack<Character> stack = new Stack<>();
        for (char c: s.toCharArray()) {
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            }
            if (c == ')') {
                if(stack.isEmpty() || stack.pop() != '(') {
                    return false;
                }
            }
            if (c == '}') {
                if (stack.isEmpty() || stack.pop() != '{') {
                    return false;
                }
            }
            if (c == ']') {
                if (stack.isEmpty() || stack.pop() != '[') {
                    return false;
                }
            }
        }
        //注意返回值,上述过程没返回false也不代表完全正确,因为可能左括号还没消除完,所以还得看它是否为空
        return stack.isEmpty();
    }
}

最小栈

在这里插入图片描述
思路
在这里插入图片描述
借助一个辅助栈来记录最小值!
为什么不使用一个整型记录最小值呢?
因为这样的话,如果那个值被弹出了,整型记录就失效了,更新会比较麻烦!
代码

class MinStack {

    /** initialize your data structure here. */
    Stack<Integer> stack;
    Stack<Integer> minStack;
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    
    public void push(int x) {
        stack.push(x);
        if (minStack.isEmpty() || x < minStack.peek()) {
            minStack.push(x);
        } else {
            minStack.push(minStack.peek());
        }
    }
    
    public void pop() {
        minStack.pop();
        stack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

用两个栈实现队列

在这里插入图片描述
直接上代码(结合注释看)

class MyQueue {

    Stack<Integer> stack1;
    Stack<Integer> stack2;
    /** Initialize your data structure here. */
    public MyQueue() {
        stack1 = new Stack<Integer>();
        stack2 = new Stack<Integer>();
    }
    
    public void push(int x) {
        //装入只往第一个栈装
        stack1.push(x);
    }
    
    //弹出时,如果第二个栈有值则直接冲第二个栈弹出,如果没有,那么就把第一个栈所有值都压入到第二个栈中
    public int pop() {
        if(empty()) {
            return -1;
        }
        if (stack2.isEmpty()) {
            while(!stack1.isEmpty()) {  
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
    
    //查看和弹出同理
    public int peek() {
        if(empty()) {
            return -1;
        }
        if (stack2.isEmpty()) {
            while(!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }
    
    /** Returns whether the queue is empty. */
    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }
    
}

搜索旋转排序数组

在这里插入图片描述
代码

class Solution {
    public int search(int[] num, int target) {
		if (num == null || num.length == 0) {
            return -1;
        }
        int start = 0;
        int end = num.length - 1;
        int mid;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (num[mid] == target) {
                return mid;
            }
            //变化的就是这两个逻辑
            if (num[mid] > num[start]) {
                if (target <= num[mid] && num[start] <= target) {
                    end = mid;
                } else {
                    start = mid;
                }
            } else {
                if (target >= num[mid] && target <= num[end]) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
        }
        if (num[start] == target) {
            return start;
        }
        if (num[end] == target) {
            return end;
        }
        return -1;
	}
}

寻找峰值(快手面试真题)

在这里插入图片描述
代码

class Solution {
    public int findPeakElement(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while(start + 1 < end) {
            mid = start + (end - start) / 2;
            if (nums[mid] < nums[mid - 1]) {
                end = mid;
            } else if (nums[mid] < nums[mid + 1]) {
                start = mid;
            } else {
                return mid;
            }
        }
        return nums[start] > nums[end] ? start : end;
    }
}

两数之和 II - 输入有序数组

在这里插入图片描述
思路
在这里插入图片描述
直接双指针法,因为有序,如果当前双指针之和大于要求的数,那么就右指针向左走,如果和小于要求的数,那么左指针就向右走
代码

class Solution {
    public int[] twoSum(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        int[] result = {-1, -1};
        int start = 0;
        int end = nums.length - 1;
        while (start < end) {
            if (nums[start] + nums[end] < target) {
                start++;
            } else if (nums[start] + nums[end] > target) {
                end--;
            } else {
                result[0] = start + 1;
                result[1] = end + 1;
                break;
            }
        }
        return result;
    }
}

如果不止要求1对符合要求的数,那么找到一对后,就让左指针和右指针同时向中间走

有效三角形的个数

在这里插入图片描述
代码

class Solution {
    public int triangleNumber(int[] nums) {
        if (nums == null || nums.length < 3) {
            return 0;
        }
        Arrays.sort(nums);
        int total = 0;
        //最大值从右到左
        for (int k = nums.length - 1; k >= 2; k--) {
            int start = 0;
            int end = k - 1;
            while (start < end) {
                //如果大于,那么start到end的中间的所有值+end都满足
                if (nums[start] + nums[end] > nums[k]) {
                    total += (end - start);
                    end --;
                }
                //如果小于,那么start就往右边走一个
                else
                    {
                    start++;
                }
            }
        }
        return total;
    }
}

1.数值的整次方

题目描述
实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

示例 1:
输入: 2.00000, 10
输出: 1024.00000

示例 2:
输入: 2.10000, 3
输出: 9.26100

示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25

思路
注意指数为负数的情况,且注意0的0次方是没有意义的,返回1或0都是可以的
优化的公式
当n为偶数时,a的n次方=a的n/2次方a的n/2次方
当n为奇数时,a的n次方=a的(n-1)/2次方
a的(n-1)/2次方*a

代码

class Solution {
    public double myPow(double x, int n) {
        //底数为0直接返回
        if(x == 0) return 0;
        long b = n;
        double res = 1.0;
        //指数小于0
        if(b < 0) {
            x = 1 / x;
            b = -b;
        }
        while(b > 0) {
        //&是与运算 2的2进制 10  1的2进制01 10&01结果为0  3的2进制 11 11&01结果为1
        //b&1==1的话它就是奇数,b&1==0为偶数
        //若b为偶数,这个只执行一次即b最后为1的时候,把x的值赋给res
            if((b & 1) == 1) res *= x;
            x *= x;
            b >>= 1;
        }
        return res;
    }
}

2.打印从1到最大的n位数

题目描述
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

示例 1:
输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]

说明:
用返回一个整数列表来代替打印
n 为正整数

待更

3.删除链表的节点

题目描述
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。

注意:此题对比原题有改动

示例 1:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

代码

//注意 这个题目前提条件是肯定能找到该节点
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
         //如果删除头结点是特殊情况,其它的删除中间节点和尾结点是一样的
         //删除头结点直接返回头结点的后面那个指针,不管是空还是非空
         if(head.val==val){
            return head.next;
        }
        ListNode mySearch=head;
        //记录mySearch前面的节点
        ListNode preMySearch=null;
        while(mySearch.val!=val){
            preMySearch=mySearch;
            mySearch=mySearch.next;
        }
        //删除节点
        if(preMySearch!=null){
            preMySearch.next=mySearch.next;
        }
        return head;
    }
}

原题是在O(1)的时间内删除链表节点,已知头结点和要删除节点的指针。

思路
不要去遍历,而是选择把要删除节点的后面那个节点复制给删除节点,然后删除节点指向后面的节点的后面的节点,这样就是在O(1)时间内了。但是特殊情况删除尾部节点就还是需要遍历了

4.正则表达式匹配

题目描述
请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。

示例 1:

输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。

示例 2:

输入:
s = “aa”
p = “a*”
输出: true
解释: 因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。

示例 3:

输入:
s = “ab”
p = “."
输出: true
解释: ".
” 表示可匹配零个或多个(’*’)任意字符(’.’)。

示例 4:

输入:
s = “aab”
p = “cab”
输出: true
解释: 因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。

示例 5:

输入:
s = “mississippi”
p = “misisp*.”
输出: false
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 ,无连续的 '’。

思路
待更

5.表示数值的字符串

待更

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值