必备的手写算法题(一)

[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<Integer> preOrder(TreeNode root) {
        if (root != null) {
            preList.add(root.val);
            preOrder(root.left);
            preOrder(root.right);
        }
        return preList;
      }

中序遍历

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

后序遍历

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

其他

用栈实现队列

算法实现
public class Leet232 {

    private Stack<Integer> input;
    private Stack<Integer> 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<Integer> q1;
    Queue<Integer> 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<Integer, Node> 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<K, V> extends LinkedHashMap<K, V> {

    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<K, V> 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;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;不稳定:如果a原本在b的前面,而a=b,排序之后a可...
    意识流丶阅读 3,162评论 2 9
  • Ba la la la ~ 读者朋友们,你们好啊,又到了冷锋时间,话不多说,发车! 1.冒泡排序(Bub...
    王饱饱阅读 1,797评论 0 7
  • 简单排序 插入排序 想象一下插队的过程... 一个人通过插队的方式排到前面,而将原本在他前面的人挤到了后面的位置。...
    Kasheem_Lew阅读 1,525评论 0 4
  • 排序算法说明 (1)排序的定义:对一序列对象根据某个关键字进行排序; 输入:n个数:a1,a2,a3,…,an 输...
    code武阅读 660评论 0 0
  • 列位看客,大家好!我就是那只“网红”警犬,大号“老三”的就是。想必大家对我的事迹已经有所耳闻。这件事情给我的狗生带...
    爱伦坡阅读 569评论 0 3