leetcode2

160.给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
 

提示:

listA 中节点数目为 m
listB 中节点数目为 n
1 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]
 

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

考虑哈希表,先存a,然后遍历b,如果哈希表中包含节点,返回该节点的值。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Map<Integer,ListNode> hashMap=new HashMap<Integer,ListNode>();
        ListNode a=headA;
        ListNode b=headB;
        int i=0;
        while(a!=null){
            hashMap.put(i,a);
            a=a.next;
            i++;
        }
        //A链表全部存入hash表中
        while(b!=null){
            if(hashMap.containsValue(b)){
                return b;
            }
            b=b.next;
        }
        return null;
    }
}

双指针可以降低空间复杂度。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode a=headA;
        ListNode b=headB;
        if(a==null||b==null){
            return null;
        }
        while(a!=b){
            if(a==null&&b==null){
                return null;
            }
            a=a==null?headB:a.next;
            b=b==null?headA:b.next;
        }
        return a;
    }
}

 24.给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:


输入:head = [1,2,2,1]
输出:true
示例 2:


输入:head = [1,2]
输出:false
 

提示:

链表中节点数目在范围[1, 105] 内
0 <= Node.val <= 9
 

进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

class Solution {
    public boolean isPalindrome(ListNode head) {
        //获取链表长度
        int num=0;
        ListNode temp=head;
        while(temp!=null){
            temp=temp.next;
            num++;
        }
        //此时temp指向链表末端,num为链表长度
        //获取前半段链表并反转,双指针遍历两链表观察val是否一致。
        ListNode t1=head;
        Stack<ListNode> s=new Stack<ListNode>();
        for(int i=0;i<num/2;i++){
            s.push(t1);
            t1=t1.next;
        }
       if(num%2!=0){
           t1=t1.next;
       }
       //t1指向链表中间
       while(t1!=null){
           if(s.peek().val!=t1.val){
               return false;
           }
           s.pop();
           t1=t1.next;
       }
        return true;
    }
}

 使用栈来获取前半段链表的反转以后的值。

Other ways:1.复制到数组使用双指针判断是否回文-非常朴素的方法;2.递归;3.快慢指针

class Solution {
    public boolean isPalindrome(ListNode head) {
        //全部存入数组
        ListNode temp=head;
        List<Integer> list=new ArrayList<Integer>();
        while(temp!=null){
            list.add(temp.val);
            temp=temp.next;
        }
        int front=0;
        int tail=list.size()-1;
        while(front<tail){
            if(!list.get(front).equals(list.get(tail))){
                return false;
            }
            front++;
            tail--;
        }
        return true;
    }
}

递归研究一下;笑死,根本没学会。

class Solution {
    private ListNode frontPointer;

    private boolean recursivelyCheck(ListNode currentNode) {
        //只要当前指针没有遍历完链表,就继续走
       if(currentNode!=null){
           //从最后一个节点开始遍历,如果最后一个节点的值和前面节点的值不相同,直接false,
           //直到最后一个节点走到了头结点,那么true
           if(!recursivelyCheck(currentNode.next)){
               return false;
           }
           if(currentNode.val!=frontPointer.val){
               return false;
           }
           frontPointer=frontPointer.next;
       }
       return true;
    }

    public boolean isPalindrome(ListNode head) {
        //全局变量指向头
        frontPointer=head;
        return recursivelyCheck(head);
    }
}

 20.给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
 

示例 1:

输入:s = "()"
输出:true
示例 2:

输入:s = "()[]{}"
输出:true
示例 3:

输入:s = "(]"
输出:false
示例 4:

输入:s = "([)]"
输出:false
示例 5:

输入:s = "{[]}"
输出:true
 

提示:

1 <= s.length <= 104
s 仅

由括号 '()[]{}' 组成

class Solution {
    public boolean isValid(String s) {
        //思路:类似中缀表达式做计算器
        //创建一个栈,遍历字符串,如果字符串为空,直接压栈,指向下一个;
        //如果字符串不为空,看当前字符和栈顶字符是否匹配,如果不匹配,压栈,指向下一个;
        //如果匹配,弹栈,指向下一个;最终如果栈为空返回true,否则返回false;
        Stack<String> stack=new Stack<String>();
        int index=0;
        while(index<s.length()){
            String c=s.substring(index,index+1);
            if(stack.size()==0){
                stack.push(c);
            }else{
                if(("(".equals(stack.peek()))&&(")".equals(c))){
                    stack.pop();
                }else if(("[".equals(stack.peek()))&&("]".equals(c))){
                    stack.pop();
                }else if(("{".equals(stack.peek()))&&("}".equals(c))){
                    stack.pop();
                }else{
                    stack.push(c);
                }
            }
            index++;
        }
        if(stack.size()==0){
            return true;
        }else{
            return false;
        }
    }
}

注意:判断栈是否为空得用size为0,不能用null,不然会报空栈异常,是jdk版本问题;判断字符串的值是否相等得用equals,再用双等号我是sb。

 70.假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
 

提示:

1 <= n <= 45

class Solution {
    public int climbStairs(int n) {
        //思路:第一反应是递归,爬到n层的方法有两种
        //1.爬1层和爬到n-1层的方法次数;2.爬2层和爬到n-2层的方法次数
        int res=0;
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        return climbStairs(n-1)+climbStairs(n-2);
    }
}

 对是对了,显示超时,明天研究一下其他方法!

1.  动态规划法  

class Solution {
    public int climbStairs(int n) {
        int a=1;
        int b=1;
        int result=1;
        for(int i=1;i<n;i++){
            result=a+b;
            a=b;
            b=result;
        }
        return result;
    }
}

 2.斐波那契数列:注意,采用round做四舍五入后再强转,计算n要用n+1。

class Solution {
    public int climbStairs(int n) {
        //斐波那契数列
        double a=Math.sqrt(5);
        double b=Math.pow((1+a)/2,n+1)-Math.pow((1-a)/2,n+1);
        return (int)Math.round(b/a);
    }
}

 3.矩阵快速幂,听都没听过。

 散了吧,学会了也写不出来!

169. 多数元素

难度简单1341收藏分享切换为英文接收动态反馈

给定一个大小为 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入:[3,2,3]
输出:3

示例 2:

输入:[2,2,1,1,1,2,2]
输出:2

进阶:

  • 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。

第一反应是排序和哈希表。 

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
}

 哈希表的遍历有一点忘记了。

class Solution {
    public int majorityElement(int[] nums) {
        //用哈希表统计每个数的出现次数,然后寻找哈希表中最大的数对应的key
        Map<Integer,Integer> hashMap=new HashMap<Integer,Integer>();
        for(int i:nums){
            if(hashMap.containsKey(i)){
                //有这个数,让该key的value+1;
                hashMap.put(i,hashMap.get(i)+1);
            }else{
                //没有这个数,把这个数作为key,给value一个1
                hashMap.put(i,1);
            }
        }
        //哈希表存完了,开始找哈希表里value最大的key
        Iterator i=hashMap.entrySet().iterator();
        int max=0;
        int maxIndex=0;
        //遍历哈希表-转set集合,用迭代器
        Set<Map.Entry<Integer,Integer>> set=hashMap.entrySet();
        for(Map.Entry<Integer,Integer> s:set){
            if(s.getValue()>max){
                max=s.getValue();
                maxIndex=s.getKey();
            }
        }
        return maxIndex;
    }
}

1.随机化-什么阴间解法?画蛇添足了属于是

class Solution {
    private int randRange(Random rand, int min, int max) {
        return rand.nextInt(max - min) + min;
    }

    private int countOccurences(int[] nums, int num) {
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == num) {
                count++;
            }
        }
        return count;
    }

    public int majorityElement(int[] nums) {
        Random rand = new Random();

        int majorityCount = nums.length / 2;

        while (true) {
            int candidate = nums[randRange(rand, 0, nums.length)];
            if (countOccurences(nums, candidate) > majorityCount) {
                return candidate;
            }
        }
    }
}

 分治和投票算法还没学过。

2.投票算法-如果不是众数当选,众数投反对票,一定会发生换届;直到众数当选,最终众数获票一定大于0.

class Solution {
    public int majorityElement(int[] nums) {
        //投票算法搞一哈
        int number=0;
        int count=0;
        for(int i:nums){
            if(count==0){
                number=i;
            }
            if(i==number){
                count++;
            }else{
                count--;
            }
    
        }
        return number;
    }
}

3.分治算法

 学完再来做。。

 283.给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:

输入: nums = [0]
输出: [0]
 

提示:

1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
 

进阶:你能尽量减少完成的操作次数吗?

第一想法类似冒泡排序。

class Solution {
    public void moveZeroes(int[] nums) {
        //暴力解法-类似冒泡排序,把所有为0的泡泡和右边的泡泡交换
        for(int i=0;i<nums.length-1;i++){
            for(int j=i+1;j<nums.length;j++){
                if(nums[i]==0){
                    //交换
                    nums[i]=nums[j];
                    nums[j]=0;
                }
            }
        }
    }
}

1.双指针-左指针指向头不动,右指针一旦遇到不为0的数就丢给左指针,左指针移动,当右指针走到末尾时,将左指针与末尾之间的所有数置0.

class Solution {
    public void moveZeroes(int[] nums) {
        //双指针搞一哈
        int left=0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=0){
                nums[left]=nums[i];
                left++;
            }
        }
        for(int i=left;i<nums.length;i++){
            nums[i]=0;
        }
    }
}

155.设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
 

示例 1:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.
 

提示:

-231 <= val <= 231 - 1
pop、top 和 getMin 操作总是在 非空栈 上调用
push, pop, top, and getMin最多被调用 3 * 104 次

 1.考虑用空间换时间

class MinStack {
    Stack<Integer> stack;
    Stack<Integer> minStack;
    public MinStack() {
        stack=new Stack<Integer>();
        minStack=new Stack<Integer>();
    }
    
    public void push(int val) {
        stack.push(val);
        if(minStack.size()!=0){
            minStack.push(Math.min(val,minStack.peek()));
        }else{
            minStack.push(val);
        }
        
    }
    
    public void pop() {
        stack.pop();
        minStack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

 //minStack.push(Integer.MAX_VALUE);最大的Integer

注意:初始化要写成成员变量;判断栈空要用size为0;警惕空栈调用peek。

2.不允许使用额外空间的情况

-底层不写栈,写链表。注意点在于next要指向前一个节点。

class MinStack {
    public Node head;
    public void push(int x) {
        if(head==null){
            head=new Node(x,x);
        }else{
            head=new Node(x,Math.min(x,head.min),head);
        }
    }

    public void pop() {
        head=head.next;
    }

    public int top() {
        return head.val;
    }

    public int getMin() {
        return head.min;
    }
}
class Node{
    public Node next;
    public int val;
    public int min;
    Node(){

    }
    Node(int val,int min){
        this.val=val;
        this.min=min;
        this.next=null;
    }
    Node(int val,int min,Node temp){
        this.val=val;
        this.min=min;
        this.next=temp;
    }
}

-保存差值,以后再研究。

 leetcode 155. 最小栈 ——O(1)额外空间_jokerMingge的博客-CSDN博客

11.给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例 1:

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:

输入:height = [1,1]
输出:1
 

提示:

n == height.length
2 <= n <= 105
0 <= height[i] <= 104

1.双指针,每次让小的那个数的指针移动。 

class Solution {
    public int maxArea(int[] height) {
        int square=0;
        int left=0;
        int right=height.length-1;
        while(left<right){
            int temp=(right-left)*(Math.min(height[right],height[left]));
            if(square<temp){
                square=temp;
            }
            if(height[left]>height[right]){
                right--;
            }else{
                left++;
            }
        }
        return square;
    }
}

 121.给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
 

提示:

1 <= prices.length <= 105
0 <= prices[i] <= 104

感觉和上面那个题有异曲同工之妙!

class Solution {
    public int maxProfit(int[] prices) {
        int maxMoney=0;
        int min=Integer.MAX_VALUE;
        int j;
        for(int i=0;i<prices.length;i++){
            if(prices[i]<min){
                min=prices[i];
            }else if(prices[i]-min>maxMoney){
                 //记录了前面的最大利润,哪怕后面min发生改变,只要没有超过前面的最大利润,不影响解答
                //譬如 2 8 1 3,虽然后面min里记录的是1,可是最大利润一直记录的是8-2=6;
                maxMoney=prices[i]-min;
            }
        }
        return maxMoney;
    }
}

 maxMoney记录了前面的最大利润,哪怕后面min发生改变,只要没有超过前面的最大利润,不影响解答。譬如 2 8 1 3,虽然后面min里记录的是1,可是最大利润一直记录的是8-2=6。(!!!)

64.给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例 1:


输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
示例 2:

输入:grid = [[1,2,3],[4,5,6]]
输出:12
 

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 200
0 <= grid[i][j] <= 100

感觉类似迷宫问题,第一反应是递归法,暴力的话应该是遍历所有路径找到最小的?结果官方给的是动态规划。

class Solution {
    public int minPathSum(int[][] grid) {
        //格子有三种,上面一排、左边一列和其他。
        //上面一排只能从他的左边移动过来
        //左边一列只能从上面移动过来
        //其他就要判断最小路径了
        int[][] dp=grid;//dp里面存走到每个点的最小路径
        dp[0][0]=grid[0][0];
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[i].length;j++){
                if(j==0&&i>0){
                    dp[i][0]=dp[i-1][0]+grid[i][0];
                }
                if(i==0&&j>0){
                    dp[0][j]=dp[0][j-1]+grid[0][j];
                }
                if(i>0&&j>0){
                    dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
                }
            }
        }
        int x=grid.length-1;
        int y=grid[0].length-1;
        return grid[x][y];
    }
}

 没有考虑输入为null的情况!!!!而且可以先写好第一行和第一列,可以降低复杂度。

class Solution {
    public int minPathSum(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int rows = grid.length, columns = grid[0].length;
        int[][] dp = new int[rows][columns];
        dp[0][0] = grid[0][0];
        for (int i = 1; i < rows; i++) {
            dp[i][0] = dp[i - 1][0] + grid[i][0];
        }
        for (int j = 1; j < columns; j++) {
            dp[0][j] = dp[0][j - 1] + grid[0][j];
        }
        for (int i = 1; i < rows; i++) {
            for (int j = 1; j < columns; j++) {
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[rows - 1][columns - 1];
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,Leetcode 2 "两数相加"是一个涉及链表的问题。该问题给定了两个非负整数,每个整数的每一位都是按照逆序的方式存储在链表中。我们需要将这两个链表相加,并返回一个新的链表作为结果。 具体解题思路可以使用迭代法或递归法来解决。迭代法的伪代码如下所示: ``` 初始化一个哑节点 dummy 和一个进位 carry,同时把两个链表的头节点分别赋值给 p 和 q 遍历链表,直到 p 和 q 都为 None 计算当前的和 sum 为 p.val + q.val + carry 计算当前的进位 carry 为 sum // 10 创建一个新节点 node,节点的值为 sum % 10 把新节点连接到结果链表的尾部 更新 p 和 q 分别为 p.next 和 q.next 如果最后还有进位 carry,则创建一个新节点 node,节点的值为 carry,并连接到结果链表的尾部 返回结果链表的头节点 dummy.next ``` 递归法的伪代码如下所示: ``` 定义一个辅助函数 addTwoNumbersHelper,输入为两个链表的头节点 p 和 q,以及进位 carry 如果 p 和 q 都为 None 且 进位 carry 为 0,则返回 None 计算当前的和 sum 为 p.val + q.val + carry 计算当前的进位 carry 为 sum // 10 创建一个新节点 node,节点的值为 sum % 10 设置新节点的下一个节点为递归调用 addTwoNumbersHelper(p.next, q.next, carry) 返回新节点 返回 addTwoNumbersHelper(p, q, 0) 的结果 以上是解决 Leetcode 2 "两数相加"问题的两种方法。如果你还有其他相关问题,请
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值