CodeTop算法

130 阅读5分钟

1.链表反转。给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode now = head;

        while(now != null){
            ListNode next = now.next;
            //next指针先指向头结点的下一个节点

            now.next = pre;
            //当前节点的指针指向前一个结点,

            pre = now;
            now = next;
        }
        head = pre;
        return pre;
    }
}

2 LRU 缓存(待做)

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

if (i != 0) {
                // 左指针向右移动一格,移除一个字符
                occ.remove(s.charAt(i - 1));
            }
            while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
                // 不断地移动右指针
                occ.add(s.charAt(rk + 1));
                ++rk;
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = Math.max(ans, rk - i + 1);
        }
        return ans;

3 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        for(int i = 0;i < n;i++){
            nums1[m + i] = nums2[i];
            //m++;
        }
        Arrays.sort(nums1);
    }
}
class Solution { 
    public void merge(int[] nums1, int m, int[] nums2, int n) { 
        int p1 = m - 1; 
        int p2 = n - 1; 
        int tail = m + n - 1; 
        int curr = 0; 
        
        while(p1 >= 0 || p2 >= 0 ){ 
        if(p1 == -1){ 
            curr = nums2[p2]; p2--; 
        }else if(p2 == -1){ 
            curr = nums1[p1]; p1--; 
        }else if(nums1[p1] > nums2[p2]){ 
            curr = nums1[p1]; p1--; 
        }else{ 
            curr = nums2[p2]; p2--; 
        } 
        nums1[tail--] = curr; 
        } 
    }
}

4 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6 。


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


输入: nums = [5,4,-1,7,8]
输出: 23

class Solution {
    public int maxSubArray(int[] nums) {
        int pre = 0;
        int maxAns = nums[0];

        for(int i:nums){

            //当前和.取{当前值,之前和}最大值
            pre = Math.max(i,pre + i);

            //最大和取{当前和,当前值}最大值
            maxAns = Math.max(pre, maxAns);
        }
        return maxAns;
    }
}

5 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]
class Solution {
    public int[] twoSum(int[] nums, int target) {

        int[] point = new int[2];
        for(int i = 0;i < nums.length;i++){
            for(int j = i + 1;j < nums.length;j++){
                if(target == nums[i] + nums[j]){
                    point[0] = i;
                    point[1] = j;
                }
            }
        }
        return point;
    }
}
class Solution {
    public int[] twoSum(int[] nums, int target) {

        //Integer, Integer:数值,下标
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for(int i = 0;i < nums.length;i++){
            if(map.containsKey(target - nums[i])){
                return new int[]{map.get(target - nums[i]),i};
            }
            map.put(nums[i],i);
        }
        return new int[0];
    }
}

6 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的

示例 1: image.png

输入:l1 = [1,2,4], l2 = [1,3,4]

输出:[1,1,2,3,4,4]

示例 2:
输入:l1 = [], l2 = []
输出:[]

示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode prehead = new ListNode(-1);
        ListNode pre = prehead;


        while(list1 != null && list2 != null){
            if(list2.val <= list1.val){
                //p2插入
                pre.next = list2;
                list2 = list2.next;
            }else{
                pre.next = list1;
                list1 = list1.next;
            }
            pre = pre.next;
        }
        pre.next = list1 == null?list2:list1;
        return prehead.next;
    }
}
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {

            if(list1 == null){
                return list2;
            }else if(list2 == null){
                return list1;
            }else if(list1.val < list2.val){
                //p1插入
                list1.next = mergeTwoLists(list1.next, list2);
                return list1;
            }else{
                list2.next = mergeTwoLists( list1, list2.next);
                return list2;
            }
    }
}

O(∩_∩)O qtmd

7 有效括号

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

有效字符串需满足:

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

示例 1:

输入:s = "()"
输出:true

示例 2:

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

示例 3:

输入:s = "(]"
输出:false

示例 4:

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

示例 5:

输入:s = "{[]}"
输出:true
class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();

        for(Character a:s.toCharArray()){
        if(a == '(' || a == '[' || a == '{'){
            stack.push(a);
        }
        else{
                if(stack.isEmpty()){
                    return false;
                    }
                else{
                    char temp = (char)stack.pop();
                    if((a == ')' && temp == '(') || ((a == ']' && temp == '[')) || ((a == '}' && temp == '{') )) 
                    {}
                    else {return false;}
                }
            }
        }
        if(stack.isEmpty()){ return true;}
        return false;    
    }
}
class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();

        int n = s.length();
        if (n % 2 == 1) {
            return false;
        }

        for(Character a:s.toCharArray()){
        if(a == '(' || a == '[' || a == '{'){
            stack.push(a);
        }
        else{
                if(!stack.isEmpty()){
                    char temp = (char)stack.pop();
                    if((a == ')' && temp == '(') || ((a == ']' && temp == '[')) || ((a == '}' && temp == '{') )) 
                    {continue;}
                    else {return false;}
                  }
                  return false;
            }
        }
        if(stack.isEmpty()){ return true;}
        return false;     
    }
}

class Solution {
    public boolean isValid(String s) {
        Map<Character, Character> map = new HashMap<>();
        map.put('(', ')');
        map.put('[', ']');
        map.put('{', '}');

        LinkedList<Character> stack = new LinkedList<>();
        for (char c : s.toCharArray()) {
            if (map.containsKey(c)) {
                stack.add(c);
            } else {
                if (stack.isEmpty() || map.get(stack.removeLast()) != c) {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }   
}

8#### 买卖股票的最佳时机

给定一个数组 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
//暴力算法超时


class Solution {
    public int maxProfit(int[] prices) {
        int max = 0;
        for(int i = 0;i < prices.length;i++){
            for(int j = i;j < prices.length;j++){
                if(prices[j] - prices[i] > max)
                {
                    max = prices[j] - prices[i];
                }
            }
        }
        return max;
    }
}
class Solution {
    public int maxProfit(int[] prices) {
        int max = 0;
        int min = prices[0];
        for(int i = 0;i < prices.length;i++){
           if(prices[i] < min){
               min = prices[i];
           }else if(prices[i] - min > max)
                {
                    max = prices[i] - min;
                }           
        }
        return max;
    }
}

9 相交链表

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

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

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

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

自定义评测:

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

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

image.png

image.png

image.png

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
//双指针
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){
            return null;
        }

        ListNode pa = headA, pb = headB;
        while(pa != pb){
            if(pa == null){
                pa = headB;pb = pb.next;
            }else if(pb == null){
                pb = headA;pa = pa.next;
            }else{
            pa = pa.next;pb = pb.next;
            }
        }
        return pa;
    }
}

//双指针优化
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){
            return null;
        }

        ListNode pa = headA, pb = headB;
        while(pa != pb){
            // if(pa == null){
            //     pa = headB;pb = pb.next;
            // }else if(pb == null){
            //     pb = headA;pa = pa.next;
            // }else{
            // pa = pa.next;pb = pb.next;
            // }

            pa = pa == null?headB:pa.next;
            pb = pb == null?headA:pb.next;
        }
        return pa;
    }
}
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Set<ListNode> con = new HashSet<ListNode>();

        ListNode pa = headA;
        while(pa != null){
            con.add(pa);
            pa = pa.next;
        }

        ListNode pb = headB;
        while(pb != null){
            if(con.contains(pb)){return pb;}
            pb = pb.next;
        }
        return null;
    }
}

10 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

image.png

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        Set<ListNode> circle = new HashSet<ListNode>();
        ListNode p = head;
        while(p != null ){
            if( !circle.contains(p)){
                circle.add(p);
                p = p.next;
                }else{
                    return true;
                }
        }
        return false;
    }
}