算法 - 链表、栈、队列、常见容器(Map、Set、List)

链表

public class LinkedList {

    private Node head = null;

    //获取头指针
    public Node getHead() {
        return head;
    }

    //在尾部插入元素
    public LinkedList add(int data) {
        Node node = new Node(data);
        if(head == null) {
            head = node;
            return this;
        }
        Node tmp = head;
        while(tmp.next != null) {
            tmp = tmp.next;
        }
        tmp.next = node;
        return this;
    }

    //输出链表
    public void print() {
        Node tmp = head;
        if (tmp == null) return;
        while(tmp.next != null) {
            System.out.print(tmp.data + ",");
            tmp = tmp.next;
        }
        System.out.println(tmp.data);
    }

    //链表长度
    public int length() {
        Node tmp = head;
        int i = 1;
        while(tmp.next != null) {
            tmp = tmp.next;
            i++;
        }
        return i;
    }

    //移除i处元素
    public void remove(int i) {
        if (i==0) {
            head = head.next;
            return;
        }
        if (i >= length()) {
            throw new IllegalArgumentException("越界");
        }
        Node tmp = head;
        int n = 0;
        while(head.next != null && n < i - 1) {
            tmp = tmp.next;
            n++;
        }
        if(tmp.next == null)
            tmp.next = null;
        else
            tmp.next = tmp.next.next;
    }

    /**
     *删除重复元素 
     *1.双重遍历 0(n^2)
     *2.借用HASHTABLE
     */
    public void deleteDuplecate() {
        Hashtable<Integer,Integer> hashtable = new Hashtable<Integer, Integer>();
        Node tmp = head;
        Node pre = null;
        while(tmp != null) {
            if(hashtable.containsKey(tmp.data)) {
                pre.next = tmp.next;
                tmp = tmp.next;
            }
            else {
                hashtable.put(tmp.data,1);
                pre = tmp;
                tmp = tmp.next;
            }
        }
    }

    //非递归反向链表
    public void reverseIteratively() {
        //非递归
        Node pRecersedHead = head;
        Node pNode = head;
        Node pPrev = null;
        while(pNode != null) {
            Node pNext = pNode.next;
            if(pNext == null) pRecersedHead = pNode;
            pNode.next = pPrev;
            pPrev = pNode;
            pNode = pNext;
        }
        this.head = pRecersedHead;
    }

    //递归反向链表
    public Node reverseIteratively(Node head) {
        //递归
        if(head == null || head.next == null) return head;
        else {
            Node newHead = reverseIteratively(head.next);
            head.next.next = head;
            head.next = null;
            if(head == this.head) this.head = newHead;
            return newHead;
        }
    }

    //反向输出 借用栈实现 或者递归实现
    public void printListReversely(Node node) {
        if(node != null) {
            printListReversely(node.next);
            System.out.print(node.data);
        }

    }

    //获取中间元素 使用两个指针,一个前进两步,一个前进一步
    public Node getMid() {
        Node p = head;
        Node q = head;
        while(p != null && p.next != null && p.next.next != null) {
            p = p.next.next;
            q = q.next;
        }
        return q;
    }

}

判断括号字符串是否有效

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

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 注意空字符串可被认为是有效字符串。

示例 1:

输入: “()”
输出: true

示例 2:

输入: “()[]{}”
输出: true

示例 3:

输入: “(]”
输出: false

示例 4:

输入: “([)]”
输出: false

示例 5:

输入: “{[]}”
输出: true

:
使用栈,若最后不为空或者出栈元素不对,则不匹配

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(c == '[' || c == '{' || c == '(') stack.push(c);
            if(c == ']') if(stack.empty() || stack.pop() != '[') return false;
            if(c == '}') if(stack.empty() || stack.pop() != '{') return false;
            if(c == ')') if(stack.empty() || stack.pop() != '(') return false;
        }
        if(!stack.empty()) return false;
        else return true;
    }
}

参考解法:
使用字符串替换

public boolean isValid(String s) {
    int length;
    do {
        length = s.length();
        s = s.replace("()","").replace("{}","").replace("[]","");
    } while (s.length() != length);
    return s.length() == 0;
}

栈实现队列

使用栈实现队列的下列操作:
  • push(x) – 将一个元素放入队列的尾部。
  • pop() – 从队列首部移除元素。
  • peek() – 返回队列首部的元素。
  • empty() – 返回队列是否为空。

示例:

MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false

说明:

  • 你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

:
使用两个栈,进队列时入栈1,出队列时若站栈2不为空则出栈,否则栈1全部元素入栈2

class MyQueue {

    private Stack<Integer> stackIn = new Stack<Integer>();
    private Stack<Integer> stackOut = new Stack<Integer>();

    /** Initialize your data structure here. */
    public MyQueue() {

    }

    /** Push element x to the back of queue. */
    public void push(int x) {
        stackIn.push(x);
    }

    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        if(stackOut.empty()) {
            while (!stackIn.empty()) {
                stackOut.push(stackIn.pop());
            }
        }
        return stackOut.pop();
    }

    /** Get the front element. */
    public int peek() {
        if(stackOut.empty()) {
            while (!stackIn.empty()) {
                stackOut.push(stackIn.pop());
            }
        }
        return stackOut.peek();
    }

    /** Returns whether the queue is empty. */
    public boolean empty() {
        return stackIn.empty() && stackOut.empty();
    }
}

返回数据流第k大

设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。

你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。

示例:

int k = 3;
int[] arr = [4,5,8,2];
KthLargest kthLargest = new KthLargest(3, arr);
kthLargest.add(3); // returns 4
kthLargest.add(5); // returns 5
kthLargest.add(10); // returns 5
kthLargest.add(9); // returns 8
kthLargest.add(4); // returns 8

:
方法1:排序 O(Nklog(k))
方法2:维护含k个元素的小顶堆 O(Nlogk)

class KthLargest {

    private int k = 0;

    private PriorityQueue<Integer> priorityQueue;

    public KthLargest(int k, int[] nums) {
        this.k = k;
        priorityQueue = new PriorityQueue<Integer>(k);
        for(int i : nums) add(i);
    }

    public int add(int val) {
        if(priorityQueue.size() < k) priorityQueue.add(val);
        else if (val >= priorityQueue.peek()) {
            priorityQueue.poll();
            priorityQueue.add(val);
        }
        return priorityQueue.peek();
    }
}

滑动窗口最大值

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。

返回滑动窗口最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

:
方法一:维护小顶堆(优先队列) O(Nlogk)
方法二:维护双向队列 O(N)

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0) return new int[]{};
        LinkedList<Integer> queue = new LinkedList<>();
        int[] outPut = new int[nums.length - k + 1];
        for (int i = 0; i < k-1; i++) add(queue,i,nums);
        for (int i = k-1; i < nums.length; i++) {
            add(queue,i,nums);
            if(queue.peek() <= i-k) queue.poll();
            outPut[i-k+1] = nums[queue.peek()];
        }
        return outPut;
    }

    private void add(LinkedList<Integer> queue,int i,int[] nums) {
        while (!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]) queue.pollLast();
        queue.addLast(i);
    }
}

相交链表

编写一个程序,找到两个单链表相交的起始节点。

:
先判断是否相交
计算两者长度
长度差即两者头节点距离公共交点的距离差

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;
        ListNode pA = headA;
        ListNode pB = headB;
        int lenA = 1;
        int lenB = 1;
        while (pA.next != null) {
            pA = pA.next;
            lenA++;
        }
        while (pB.next != null) {
            pB = pB.next;
            lenB++;
        }
        if(pA != pB) return null;
        ListNode tA = headA;
        ListNode tB = headB;
        if(lenA > lenB) {
            int d = lenA - lenB;
            while(d!=0) {
                tA = tA.next;
                d--;
            }
        }else {
            int d = lenB - lenA;
            while (d!=0) {
                tB = tB.next;
                d--;
            }
        }
        while(tA != tB) {
            tA = tA.next;
            tB = tB.next;
        }
        return tA; 
    }
}

有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。

示例 1:

输入: s = “anagram”, t = “nagaram”
输出: true

示例 2:

输入: s = “rat”, t = “car”
输出: false

说明:
你可以假设字符串只包含小写字母。

进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?

:
方法1. 排序 然后比较排序好的字符串 O(nlog(n))
方法2. 使用Map计数 O(N)

public boolean isAnagram(String s, String t) {
        Map<Character,Integer> maps = new HashMap<Character, Integer>();
        Map<Character,Integer> mapt = new HashMap<Character, Integer>();
        if(s.length() != t.length()) return false;
        for (int i = 0; i < s.length(); i++) {
            Character sc = s.charAt(i);
            Character tc = t.charAt(i);
            if(maps.containsKey(sc)) maps.put(sc,maps.get(sc)+1);
            else maps.put(sc,1);
            if(mapt.containsKey(tc)) mapt.put(tc,mapt.get(tc)+1);
            else mapt.put(tc,1);
        }
        return maps.equals(mapt);
    }

两数之和

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

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:

给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

:
方法1. 暴力循环 O(N^2)
方法2. 使用Set O(N)
方法3. 两遍哈希表 一遍插入,一遍查找 O(N)
方法4. 一遍哈希表 插入同时查找

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement)) {
                return new int[] { map.get(complement), i };
            }
            map.put(nums[i], i);
        }
        throw new IllegalArgumentException("No two sum solution");
    }
}

三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

:
1.暴力循环 O(N^3)
2.使用Set O(N^2)
3.先排序O(Nlog(N)),后查找,枚举一个数,另外两个数从后面的数组两边往中间夹 O(N^2)

public List<List<Integer>> threeSum(int[] nums) {
        Set<List<Integer>> set = new HashSet<>();
        Arrays.sort(nums);
        int length = nums.length;
        for (int i = 0; i < length-2; i++) {
            if (i != 0 && nums[i-1] == nums[i]) continue;
            int min = i + 1;
            int max = length-1;
            while(min != max) {
                if(nums[i] + nums[min] + nums[max] == 0) {
                    set.add(Arrays.asList(nums[i], nums[min], nums[max]));
                    min = min + 1;
                } else if(nums[i] + nums[min] + nums[max] < 0)
                    min = min + 1;
                else max = max - 1;
            }
        }
        return new ArrayList<>(set);
    }

猜你喜欢

转载自blog.csdn.net/qq_37873426/article/details/89417682
今日推荐