必备的手写算法题(一)

[TOC]

排序算法

冒泡排序

算法步骤
  • 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
算法复杂度

最好复杂度O(n),最坏复杂度O(n2),空间复杂度O(1),是稳定排序

算法实现
    public static int[] sort(int[] sourceArray) {
        int[] array = Arrays.copyOf(sourceArray, sourceArray.length);

        for (int i = 0; i < array.length; i++) {
            // 设置一个标记,如果为true,代表本轮没有元素交换,已经有序
            boolean flag = true;
            for (int j = i; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j + 1];
                    array[j + 1] = array[j];
                    array[j] = temp;
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
        }
        return array;
    }

插入排序

算法步骤
  • 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
  • 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面)
算法复杂度

平均复杂度O(n2), 最好复杂度O(n),最坏复杂度O(n2),空间复杂度O(1),稳定排序

算法实现
    public static int[] sort(int[] sourceArray) {
        int[] array = Arrays.copyOf(sourceArray, sourceArray.length);

        // 从下标为1的位置开始选择插入位置,默认为0的地方已经有序
        for (int i = 1; i < array.length; i++) {

            int temp = array[i];
            int j = i;
            // 从已经排序的序列中从右边开始比较,找到比当前数小的
            while (j > 0 && temp < array[j - 1]) {
                array[j] = array[j - 1];
                j--;
            }

            // 存在比temp小的数
            if (j != i) {
                array[j] = temp;
            }

        }
        return array;
    }
    

选择排序

算法步骤
  • 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  • 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  • 重复第二步,直到所有元素均排序完毕。
算法复杂度

最坏和最好,平均复杂度都是O(n2),空间复杂度O(1),不稳定排序

算法实现
    public static int[] sort(int[] sourceArray) {
        int[] array = Arrays.copyOf(sourceArray, sourceArray.length);

        // 总共需要经过N-1轮比较
        for (int i = 0; i < array.length - 1; i++) {
            int min = i;
            for (int j = i + 1; j < array.length; j++) {
                if (array[j] < array[min]) {
                    min = j;
                }
            }
            if (min != i) {
                int temp = array[min];
                array[min] = array[i];
                array[i] = temp;
            }
        }
        return array;
    }
    

归并排序

算法步骤
  • 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  • 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  • 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  • 重复步骤 3 直到某一指针达到序列尾;
  • 将另一序列剩下的所有元素直接复制到合并序列尾。
算法复杂度

平均复杂度O(nlogn), 最好复杂度O(nlogn),最坏复杂度O(nlogn),空间复杂度O(n),稳定排序

算法实现
    public static int[] sort(int[] sourceArray) {
        int[] array = Arrays.copyOf(sourceArray, sourceArray.length);

        if (array.length < 2) {
            return array;
        }
        int mid = (int) Math.floor(array.length / 2);

        int[] leftArray = Arrays.copyOfRange(array, 0, mid);
        int[] rightArray = Arrays.copyOfRange(array, mid, array.length);
        return merge(sort(leftArray), sort(rightArray));
    }


    public static int[] merge(int[] left, int[] right) {
        int[] result = new int[left.length + right.length];
        int i = 0;
        while (left.length > 0 && right.length > 0) {
            if (left[0] <= right[0]) {
                result[i++] = left[0];
                left = Arrays.copyOfRange(left, 1, left.length);
            } else {
                result[i++] = right[0];
                right = Arrays.copyOfRange(right, 1, right.length);
            }
        }

        while (left.length > 0) {
            result[i++] = left[0];
            left = Arrays.copyOfRange(left, 1, left.length);
        }

        while (right.length > 0) {
            result[i++] = right[0];
            right = Arrays.copyOfRange(right, 1, right.length);
        }

        return result;
    }
    

快速排序

算法步骤
  • 从数列中挑出一个元素,称为 '基准'(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
  • 在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
算法复杂度

平均复杂度O(nlogn), 最好复杂度O(nlogn),最坏复杂度O(n2),空间复杂度O(1),不稳定排序

算法实现
    public static int[] quickSort(int[] array, int left, int right) {
        if (left < right) {
            int partitionIndex = partition(array, left, right);
            quickSort(array, left, partitionIndex - 1);
            quickSort(array, partitionIndex + 1, right);
        }
        return array;
    }
    
    // 设定基准值pivot,默认最左边的为基准元素
    public static int partition(int[] array, int left, int right) {
        int pivot = left;
        int index = pivot + 1;
        for (int i = index; i <= right; i++) {
            if (array[i] < array[pivot]) {
                swap(array, i, index);
                index++;
            }
        }
        swap(array, pivot, index - 1);
        return index - 1;
    }

    public static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    

堆排序

算法步骤
  • 创建一个堆 H[0……n-1];
  • 把堆首(最大值)和堆尾互换;
  • 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
  • 重复步骤 2,直到堆的尺寸为 1。
算法复杂度

平均,最好最快都是O(logn),空间复杂度O(1),是不稳定排序

算法实现
    public static int[] sort(int[] sourceArray) {
        int[] array = Arrays.copyOf(sourceArray, sourceArray.length);

        int len = array.length;
        buildMaxHeap(array, len);

        for (int i = len - 1; i > 0; i--) {
            swap(array, 0, i);
            len--;
            heapify(array, 0, len);
        }
        return array;
    }

    private static void heapify(int[] array, int i, int len) {
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int largest = i;

        if (left < len && array[left] > array[largest]) {
            largest = left;
        }
        if (right < len && array[right] > array[largest]) {
            largest = right;
        }

        if (largest != i) {
            swap(array, i, largest);
            heapify(array, largest, len);
        }

    }

    private static void swap(int[] array, int i, int j) {
        int temp = array[j];
        array[j] = array[i];
        array[i] = temp;
    }

    private static void buildMaxHeap(int[] array, int len) {
        for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
            heapify(array, i, len);
        }
    }
    

链表

反转链表

算法实现
    
    public class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
        next = null;
    }

    }
    
    private ListNode reverseList(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode pre = null;
        while (head != null) {
            ListNode temp = head.next;
            head.next = pre;
            pre = head; // 前指针后移
            head = temp; // 当前指针后移
        }
        return pre;
    }

单链表的排序

判断链表是否有环

算法实现
    public boolean hasCycle(ListNode head) {
            if (head == null || head.next == null) {
                return false;
            }
            ListNode fastPointer = head;
            ListNode slowPointer = head;

            while (fastPointer != null && fastPointer.next != null) {
                fastPointer = fastPointer.next.next;
                slowPointer = slowPointer.next;
                if (fastPointer == slowPointer) {
                    return true;
                }
            }
            return false;

    }

二叉树

先序遍历

算法实现
/**
 * @author yujianjian  2019-04-23 17:31
 * 前序:根左右 中序:左根右 后序:左右根
 */
    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) {
            val = x;
        }
     }
     
     private List preOrder(TreeNode root) {
        if (root != null) {
            preList.add(root.val);
            preOrder(root.left);
            preOrder(root.right);
        }
        return preList;
      }

中序遍历

算法实现
    private List inOrder(TreeNode root) {
        if (root != null) {
            inOrder(root.left);
            inList.add(root.val);
            inOrder(root.right);
        }
        return inList;
    }

后序遍历

算法实现
    private List postOrder(TreeNode root) {
        if (root != null) {
            postOrder(root.left);
            postOrder(root.right);
            postList.add(root.val);
        }
        return postList;
    }

其他

用栈实现队列

算法实现
public class Leet232 {

    private Stack input;
    private Stack output;

    /**
     * 用栈实现队列的功能
     */


    /**
     * Initialize your data structure here.
     */
    public Leet232() {
        input = new Stack<>();
        output = new Stack<>();
    }

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

    /**
     * Removes the element from in front of queue and returns that element.
     */
    public int pop() {

        if (!output.empty()) {
            return output.pop();
        }
        while (!input.empty()) {
            output.push(input.pop());
        }
        return output.pop();
    }

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

    }

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

}

用队列实现栈

算法实现
public class Leet225 {

    /**
     * 使用队列实现栈的功能
     * peek或pop的时候留一个元素在另外一个队列,完事后peek的话需要再加入到另外一个队列中去
     */

    Queue q1;
    Queue q2;

    /**
     * Initialize your data structure here.
     */
    public Leet225() {
        q1 = new ArrayDeque<>();
        q2 = new ArrayDeque<>();
    }

    /**
     * Push element x onto stack.
     */
    public void push(int x) {
        if (!q1.isEmpty()) {
            q1.add(x);
        } else {
            q2.add(x);
        }
    }

    /**
     * Removes the element on top of the stack and returns that element.
     */
    public int pop() {
        if (!q1.isEmpty() && q1.size() <= 1) {
            return q1.poll();
        } else if (!q1.isEmpty()) {
            while (q1.size() > 1) {
                q2.add(q1.poll());
            }
            return q1.poll();
        } else if (!q2.isEmpty() && q2.size() <= 1) {
            return q2.poll();
        } else {
            while (q2.size() > 1) {
                q1.add(q2.poll());
            }
            return q2.poll();
        }

    }

    /**
     * Get the top element.
     */
    public int top() {
        if (!q1.isEmpty() && q1.size() <= 1) {
            return q1.peek();
        } else if (!q1.isEmpty()) {
            while (q1.size() > 1) {
                q2.add(q1.poll());
            }
            Integer value = q1.poll();
            if (value != null) {
                q2.add(value);
            }
            return value;
        } else if (!q2.isEmpty() && q2.size() <= 1) {
            return q2.peek();
        } else {
            while (q2.size() > 1) {
                q1.add(q2.poll());
            }
            Integer value = q2.poll();
            if (value != null) {
                q1.add(value);
            }
            return value;
        }

    }

    /**
     * Returns whether the stack is empty.
     */
    public boolean empty() {
        return q1.isEmpty() && q2.isEmpty();
    }

}

递归和非递归方式实现斐波那契数列

算法实现
   
   // 递归的方式实现
    public int fib2(int N) {
        if (N <= 1) {
            return N;
        }
        return fib2(N - 1) + fib2(N - 2);
    }
   
   // 非递归的方式实现
   public int fib(int N) {
        if (N <= 1) {
            return N;
        }
        int[] result = new int[N + 1];
        result[0] = 0;
        result[1] = 1;
        for (int i = 2; i < result.length; i++) {
            result[i] = result[i - 1] + result[i - 2];
        }
        return result[N];
    }

TopK问题

topk最小问题
    private static int kthSmallest(int[] arr, int k) {
        if (arr == null || arr.length < k) {
            return -1;
        }
        int partition = partition(arr, 0, arr.length - 1);
        while (partition + 1 != k) {
            if (partition + 1 < k) {
                partition = partition(arr, partition + 1, arr.length - 1);
            } else {
                partition = partition(arr, 0, partition - 1);
            }
        }
        return arr[partition];
    }
    
    private static int partition(int[] arr, int left, int right) {
        int pivot = left;
        int index = pivot + 1;

        // 小的放pivot左边,大的放右边
        for (int i = index; i <= right; i++) {
            if (arr[i] < arr[pivot]) {
                swap(arr, i, index);
                index++;
            }
        }

        swap(arr, pivot, index - 1);
        return index - 1;
    }
    
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
topk最大问题
    private static int kthLargest(int[] arr, int k) {
        if (arr == null || arr.length < k) {
            return -1;
        }
        int partition = partition2(arr, 0, arr.length - 1);
        while (partition + 1 != k) {
            if (partition + 1 < k) {
                partition = partition2(arr, partition + 1, arr.length - 1);
            } else {
                partition = partition2(arr, 0, partition - 1);
            }
        }
        return arr[partition];
    }
    
    private static int partition2(int[] arr, int left, int right) {
        int pivot = left;
        int index = pivot + 1;

        // 大的放pivot左边,小的放右边
        for (int i = index; i <= right; i++) {
            if (arr[i] > arr[pivot]) {
                swap(arr, i, index);
                index++;
            }
        }

        swap(arr, pivot, index - 1);
        return index - 1;
    }
    

实现LRU cache

不用jdk
算法实现
    class Node {
        private int key;
        private int value;
        private Node pre;
        private Node next;

        public Node(int key, int value) {
            this.key = key;
            this.value = value;
            pre = next = null;
        }
    }
    
    class LRUCache {

        private Node head;
        private Node tail;
        private int size;
        private int capacity;
        private Map map;

        public LRUCache(int capacity) {
            this.head = tail = null;
            this.size = 0;
            this.capacity = capacity;
            map = new HashMap<>((int) (capacity / 0.75) + 1);
        }

        public int get(int key) {
            Node result = map.get(key);
            if (result == null) {
                return -1;
            }
            moveToHead(result);
            return result.value;
        }

        private void moveToHead(Node node) {
            if (node == head) {
                return;
            }
            Node cur = head;
            //找到在链表中的位置
            while (cur != null && cur.key != node.key) {
                cur = cur.next;
            }
            if (cur != null) {
                Node next = cur.next;
                Node pre = cur.pre;
                pre.next = next;
                next.pre = pre;
                head.pre = node;
                head = node;
            }
        }

        public void put(int key, int value) {

            if (head == null) {
                head = new Node(key, value);
                tail = head;
                map.put(key, head);
                size++;
                return;
            } else {
                Node target = map.get(key);
                if (target != null) {
                    modify(key, value);
                } else {
                    if (size >= capacity) {
                        removeTail();
                    }
                    Node node = new Node(key, value);
                    node.next = head;
                    head.pre = node;
                    head = node;
                    map.put(key, node);
                    size++;
                }
            }
        }

        private void removeTail() {
            if (tail != null) {
                tail = tail.pre;
                tail.next = null;
            }
        }

        private void modify(int key, int value) {
            if (head.key == key) {
                head.value = value;
                map.put(key, head);
                return;
            }
            Node cur = head;
            while (cur != null && cur.key != key) {
                cur = cur.next;
            }
            if(cur != null) {
                cur.value = value;
                if(cur.next != null) {
                    cur.next.pre = cur.pre;
                }else {
                    tail = cur.pre;
                }
                cur.pre.next = cur.next;
                head.pre = cur;
                cur.next = head;
                head = cur;
                map.put(key,head);
            }
        }


    }
继承linkedHashMap
算法实现
public class LRUCache extends LinkedHashMap {

    private final int CACHE_SIZE;

    /**
     * 传递进来最多能缓存多少数据
     *
     * @param cacheSize 缓存大小
     */
    public LRUCache(int cacheSize) {
        // true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。
        super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
        CACHE_SIZE = cacheSize;
    }


    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        // 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
        return size() > CACHE_SIZE;
    }
}

有序数组元素出现的次数

// TODO,利用map存储,记录出现的次数

pow(x,n)的函数实现

算法实现
    public double myPow(double x, int n) {
        double res = 1.0;
        for (int i = n; i != 0; i /= 2) {
            if (i % 2 == 1) {
                res *= x;
            }
            x *= x;
        }
        return n < 0 ? 1 / res : res;
    }

你可能感兴趣的:(必备的手写算法题(一))