数据结构与算法之美

05 算法

概念:数组是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同类型的数据。

错误描述改正:数组是适合查找操作,但是查找的时间复杂度并不为O(1)。即便是排好序的数组,你用二分查找,时间复杂度也是O(logn)。所以正确的表述应该是,数组支持随机访问,根据下标随机访问的时间复杂度为O(1)。

06 链表

缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该保留?这就需要缓存淘汰策略来决定。常见的三种策略:先进先出策略(FIFO),最少使用策略(LFU),最近最少使用策略(LRU)。

数组与链表的区别:数组需要一块连续的内存空间来存储,链表通过指针将一组零碎的内存块串联起来使用。

写链表的代码技巧:

  1. 理解指针或引用的含义

    将某个变量赋值给引用,实际上就是将这个变量的地址赋值给引用,或者反过来说,引用中存储了这个变量的内存地址,指向了这个变量,通过引用就能找到这个变量。

  2. 警惕指针丢失和内存泄漏

  3. 利用哨兵简化实现难度

  4. 重点留意边界条件处理

    如果链表为空时,代码能否正常工作?

    如果链表只包含一个结点时,代码能否正常工作?

    如果链表只包含两个结点时,代码能否正常工作?

    代码逻辑在处理头结点和未结点的时候,能否正常工作?


单链表反转

package linkedlist;

/**
 * Description:单链表反转
 *
 * @author hanshuangwen
 * @create 2021-05-23 22:24
 */
public class SingleInversion {
  static class  Node{
    int data;
    Node next;
    public Node(int data) {
      this.data = data;
    }
  }
  public static void main(String[] args) {
    Node sentinel=new Node(-1);
    sentinel.next=null;
    Node head=sentinel;

    insertHead(head,1);
    insertHead(head,2);
    insertHead(head,3);
    insertHead(head,4);

    inverion(head);

    print(head);
  }

  private static void print(Node head) {
    Node p=head.next;
    while (p!=null){
      System.out.println(p.data);
      p=p.next;
    }
  }

  private static void inverion(Node head) {
    Node after=null;
    Node cur=head.next;
    Node before=null;
    while (cur!=null){
      before=cur.next;
      cur.next=after;
      after=cur;
      cur=before;
    }
    head.next=after;
  }

  private static void insertHead(Node head, int i) {
    Node newNode=new Node(i);
    newNode.next=head.next;
    head.next=newNode;
  }
}


删除链表倒数第n个节点


两个有序的链表合并

package linkedlist;

/**
 * Description:两个有序链表的合并
 *
 * @author hanshuangwen
 * @create 2021-05-23 22:57
 */
public class MergeLinked {
  static class Node {
    int data;
    Node next;

    public Node(int data) {
      this.data = data;
    }
  }

  public static void main(String[] args) {
    Node sentinel = new Node(-1);
    sentinel.next = null;
    Node head1 = sentinel;

    insertHead(head1, 9);
    insertHead(head1, 7);
    insertHead(head1, 5);
    insertHead(head1, 3);
    insertHead(head1, 1);

    Node head2 = sentinel;
    insertHead(head2, 8);
    insertHead(head2, 6);
    insertHead(head2, 4);
    insertHead(head2, 2);

    merge(head1, head2);


    print(head1);

  }

  private static Node merge(Node head1, Node head2) {
    if (head1.next == null || head2.next == null) {
      return head1.next == null ? head1 : head2;
    }
    Node head = head1.next.data < head2.next.data ? head1 : head2;
    Node cur1 = head == head1 ? head1.next : head2.next;
    Node cur2 = head == head1 ? head2.next : head1.next;
    Node pre = null;
    Node next = null;
    while (cur1 != null && cur2 != null) {
      if (cur1.data <= cur2.data) {
        pre = cur1;
        cur1 = cur1.next;
      } else {
        pre.next = cur2;
        next = cur2.next;
        cur2.next = cur1;
        //注意要走pre
        pre = cur2;
        cur2 = next;
      }
    }
    pre.next = cur1 == null ? cur2 : cur1;
    return head;
  }


  private static void insertHead(Node head, int i) {
    Node newNode = new Node(i);
    newNode.next = head.next;
    head.next = newNode;
  }

  private static void print(Node head) {
    Node p = head.next;
    while (p != null) {
      System.out.println(p.data);
      p = p.next;
    }
  }
}


如何基于链表实现LRU缓存淘汰算法?

思路:维护一个有序单链表,越靠近链表尾部的节点是越早之前访问的。当有一个新数据被访问时,我们从头开始顺序遍历链表。

1、如果此数据之前被缓存在链表中了,我们遍历得到这个数据对应的节点,并将其从原来的位置删除,然后再插入到链表的头部。

2、如果数据没有在缓存中,又可以分为两种情况

  • 如果此时缓存未满,则直接插入到链表的头部
  • 如果此时缓存已满,则链表尾节点删除,将新的数据节点插入链表的头部

代码:

package array;

/**
 * Description:
 *
 * @author hanshuangwen
 * @create 2021-05-21 22:53
 */
public class LruDemo {
  public static final int size=4;
  public static int count=0;
  static class  Node{
    int data;
    Node next;
    public Node(int data) {
      this.data = data;
    }
  }

  public static void main(String[] args) {
    Node sentinel=new Node(-1);
    Node head=sentinel;
    sentinel.next=null;
    insertNode(head,1);
    insertNode(head,2);
    insertNode(head,3);
    insertNode(head,4);
//    use(head,4);
    use(head,5);
//    use(head,4);
    traverse(head);




  }

  private static void traverse(Node head){
    Node p=head;
    while (p!=null){
      System.out.println(p.data);
      p=p.next;
    }
  }

  private static boolean delete(Node head,int data){
    Node p=head;
    while (p.next!=null){
      if (p.next.data==data){
        p.next=p.next.next;
        count--;
        return true;
      }
      p=p.next;
    }
    return false;
  }
  private static void deleteLast(Node head){
    Node p=head;
    Node n=head.next;
    while (p!=null&&n!=null){
      if (n.next==null){
        p.next=n.next;
        count--;
        return;
      }
      p=p.next;
      n=n.next;
    }
  }
  private static void insertNode(Node head,int num){
    if (count==size){
      throw new RuntimeException("链表已满");
    }
    count++;
    Node newNode=new Node(num);
    newNode.next=head.next;
    head.next=newNode;
  }

  private static void use(Node head, int num) {
    boolean isHave = delete(head, num);
    if (isHave){
      insertNode(head,num);
      return;
    }
    if (count<size){
      insertNode(head,num);
      return;
    }
    deleteLast(head);
    insertNode(head,num);
  }


}

用单链表判断一个字符串是否时回文字符串?

package array;

import java.util.Stack;

/**
 * Description:
 *
 * @author hanshuangwen
 * @create 2021-05-22 0:14
 */
public class ReString {
  static class Node {
    char data;
    Node next;

    public Node(char data) {
      this.data = data;
    }
  }

  public static void main(String[] args) {
    String a = "abcdba";
    judgeRe(a);
  }

  private static void judgeRe(String a) {
    StringToNode(a);
  }

  private static void insertHead(Node head, char data) {
    Node newNode = new Node(data);
    newNode.next = head.next;
    head.next = newNode;
  }

  private static void print(Node head) {
    Node p = head;
    while (p != null) {
      System.out.println(p.data);
      p = p.next;
    }
  }

  private static void StringToNode(String a) {
    Node sentinel = new Node('/');
    sentinel.next = null;
    Node head = sentinel;

    for (int i = 0; i < a.length(); i++) {
      insertHead(head, a.charAt(i));
    }
    //方法一,通过一个栈实现
//    System.out.println(isPalindrome1(head));
    //方法二:也是通过栈实现,但是只压前半部分数据进栈
//    System.out.println(isPalindrome2(head));

    System.out.println(isPalindrome3(head));

    print(head);
  }

  private static boolean isPalindrome3(Node head) {
    Node n1 = head.next;
    Node n2 = head.next;
    while (n2.next != null && n2.next.next != null) {
      n1 = n1.next;
      n2 = n2.next.next;
    }
    n2 = n1.next;
    n1.next = null;
    Node n3 = null;
    while (n2 != null) {
      n3 = n2.next;
      n2.next = n1;
      n1 = n2;
      n2 = n3;
    }
    n3 = n1;
    n2 = head.next;
    boolean res = true;
    while (n1 != null && n2 != null) {
      if (n1.data != n2.data) {
        res = false;
        break;
      }
      n1 = n1.next;
      n2 = n2.next;
    }
    n1 = n3.next;
    n3.next = null;
    while (n1 != null) {
      n2 = n1.next;
      n1.next = n3;
      n3 = n1;
      n1 = n2;
    }
    return res;
  }

  private static boolean isPalindrome2(Node head) {
    if (head.next == null || head.next.next == null) {
      return true;
    }
    //找偶数链表的右中节点,所以慢节点会从第二节点开始
    //如果要找链表的左中节点,那慢节点要从第一个节点开始
    Node fast = head.next;
    Node right = fast.next;
    while (fast.next != null && fast.next.next != null) {
      fast = fast.next.next;
      right = right.next;
    }

    Stack<Character> stack = new Stack<>();
    while (right != null) {
      stack.push(right.data);
      right = right.next;
    }
    fast = head.next;
    while (!stack.isEmpty()) {
      if (stack.pop() != fast.data) {
        return false;
      }
      fast = fast.next;
    }
    return true;
  }

  private static boolean isPalindrome1(Node head) {
    Stack<Character> stack = new Stack<>();
    Node p = head.next;
    while (p != null) {
      stack.push(p.data);
      p = p.next;
    }
    p = head.next;
    while (p != null) {
      if (p.data != stack.pop()) {
        return false;
      }
      p = p.next;
    }
    return true;
  }

}

08 栈

概念:当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,我们就应该首选“栈”这种数据结构。

  • 用数组实现的栈,叫做顺序栈
  • 用链表实现的栈,叫做链式栈
package stack;

/**
 * Description:顺序栈
 *
 * @author hanshuangwen
 * @create 2021-05-24 22:42
 */
public class ArrayStack {
  private String[] items;
  private int count;
  private int n;

  public ArrayStack(int n) {
    this.items = new String[n];
    this.count = 0;
    this.n = n;
  }

  public boolean push(String item) {
    if (count >= n) {
      resize();
    }
    items[count] = item;
    ++count;
    return true;
  }

  private void resize() {
    String[] newItems = new String[n * 2];
    System.arraycopy(items, 0, newItems, 0, n);
    items = newItems;
    n = 2 * n;
  }

  public String pop() {
    if (count == 0) {
      return null;
    }
    return items[--count];
  }

  public static void main(String[] args) {
    ArrayStack stack = new ArrayStack(3);
    stack.push("a");
    stack.push("b");
    stack.push("c");
    stack.push("d");
    stack.push("e");
    stack.push("f");
    stack.push("g");
    System.out.println(stack.pop());
  }
}

package stack;

/**
 * Description:链式栈
 *
 * @author hanshuangwen
 * @create 2021-05-25 23:42
 */
public class LinkedStack {
  Node head = null;
  int count;
  int n;

  class Node {
    int data;
    Node next;

    public Node(int data) {
      this.data = data;
    }
  }

  public LinkedStack(int size) {
    Node sentinel = new Node(-1);
    sentinel.next = null;
    head = sentinel;
    n = size;
    count = 0;
  }

  public int pop() {
    if (count == 0) {
      return -1;
    }
    --count;
    return deleteHead(head);
  }

  protected int deleteHead(Node head) {
    Node del = head.next;
    head.next = head.next.next;
    return del.data;
  }

  public boolean push(int data) {
    if (count >= n) {
      return false;
    }
    ++count;
    insertHead(head, data);
    return true;
  }

  protected void insertHead(Node head, int data) {
    Node newNode = new Node(data);
    newNode.next = head.next;
    head.next = newNode;
  }

  public static void main(String[] args) {
    LinkedStack linkedStack = new LinkedStack(3);
    linkedStack.push(1);
    linkedStack.push(2);
    linkedStack.push(3);
    System.out.println(linkedStack.pop());
  }
}

09 队列

顺序队列:用数组实现的队列

链式队列:用链表实现的队列

package queue;

/**
 * Description:顺序队列
 *
 * @author hanshuangwen
 * @create 2021-05-26 22:43
 */
public class ArrayQueue {
  private int[] items;
  private int n;
  private int head = 0;
  private int tail = 0;

  public ArrayQueue(int capacity) {
    this.n = capacity;
    items = new int[capacity];
  }

  public boolean enQueue(int data) {
    if (tail >= n) {
      if (head == 0) {
        return false;
      }
      for (int i = head; i < tail; i++) {
        items[i - head] = items[i];
      }
      head = 0;
      tail -= head;
    }
    items[tail++] = data;
    return true;
  }

  public int deQueue() {
    if (head == tail) {
      throw new RuntimeException();
    }
    return items[head++];
  }

  public static void main(String[] args) {
    ArrayQueue arrayQueue = new ArrayQueue(3);
    arrayQueue.enQueue(1);
    arrayQueue.enQueue(2);
    arrayQueue.enQueue(3);
    System.out.println(arrayQueue.deQueue());
  }
}

package queue;

/**
 * Description:链式队列
 *
 * @author hanshuangwen
 * @create 2021-05-26 22:54
 */
public class LinkedQueue {
  static class Node {
    private int data;
    private Node next;

    public Node(int data) {
      this.data = data;
    }
  }

  private int n;
  private int count = 0;
  private Node head;
  private Node tail;
  private Node sentinel;

  public LinkedQueue(int n) {
    this.n = n;
    sentinel = new Node(-1);
    sentinel.next = null;
    tail = sentinel;
  }

  public boolean enQueue(int data) {
    if (count >= n) {
      return false;
    }
    tail.next = new Node(data);
    tail = tail.next;
    head = sentinel.next;
    ++count;
    return true;
  }

  public int deQueue() {
    if (head == tail) {
      throw new RuntimeException();
    }
    Node del = head;
    sentinel.next = sentinel.next.next;
    head = sentinel.next;
    --count;
    return del.data;
  }

  public static void main(String[] args) {
    LinkedQueue linkedQueue = new LinkedQueue(3);
    linkedQueue.enQueue(1);
    linkedQueue.enQueue(2);
    linkedQueue.enQueue(3);
    System.out.println(linkedQueue.deQueue());
  }
}

package queue;

/**
 * Description:循环队列
 *
 * @author hanshuangwen
 * @create 2021-05-26 22:43
 */
public class CycleQueue {
  private int[] items;
  private int n;
  private int head = 0;
  private int tail = 0;

  public CycleQueue(int capacity) {
    this.n = capacity;
    items = new int[capacity];
  }

  public boolean enQueue(int data) {
    if ((tail + 1) % n == head) {
      return true;
    }
    items[tail] = data;
    tail = (tail + 1) % n;
    return true;
  }

  public int deQueue() {
    if (head == tail) {
      throw new RuntimeException();
    }
    int del = items[head];
    head = (head + 1) % n;
    return del;
  }

  public static void main(String[] args) {
    CycleQueue arrayQueue = new CycleQueue(3);
    arrayQueue.enQueue(1);
    arrayQueue.enQueue(2);
    arrayQueue.enQueue(3);
    System.out.println(arrayQueue.deQueue());
  }
}

10 递归

概念:去的过程叫“递“,回来的过程叫”归“。

递归需要满足的三个条件:

  1. 一个问题的解可以分为几个子问题的解
  2. 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
  3. 存在递归终止条件

写递归代码的关键:写出递推公式,找到终止条件

总结:写递归代码的关键就是找打如何将大问题分解成小问题的规律,并且基于此写出递推公式,然后推敲终止条件,最后将递推公式和终止条件翻译成代码。

递归代码两个常见问题:

  • 递归代码要警惕堆栈溢出
  • 递归代码要警惕重复计算(将已经计算过的值,放到hash表存起来)
package recursion;

/**
 * Description:累加递归
 *
 * @author hanshuangwen
 * @create 2021-05-27 23:39
 */
public class LeijiaRec {
  public static void main(String[] args) {
    //递推公式:f(n)=f(n-1)+1
    //终止条件:f(1)=1;
    int leijia = leijia(5);
    System.out.println(leijia);
    //非递归
    int i = leijiaNon(5);
    System.out.println(i);

  }

  private static int leijiaNon(int num) {
    int ret = 1;
    for (int i = 2; i <= num; i++) {
      ret += 1;
    }
    return ret;
  }

  private static int leijia(int i) {
    if (i == 1) {
      return 1;
    }
    return leijia(i - 1) + 1;
  }
}

package recursion;

import java.util.HashMap;

/**
 * Description:走台阶
 *
 * @author hanshuangwen
 * @create 2021-05-27 23:49
 */
public class Steps {
  public static void main(String[] args) {
    //递推公式:f(n)=f(n-1)+f(n-2)
    //终止条件:f(1)=1 f(2)=2
    int num = goStep(5);
    System.out.println(num);
    //非递归实现
    int num2 = goStepNon(5);
    System.out.println(num2);
  }

  private static int goStepNon(int data) {
    if (data == 1) {
      return 1;
    }
    if (data == 2) {
      return 2;
    }
    int pp = 1;
    int p = 2;
    int res = 0;
    for (int i = 3; i <= data; i++) {
      res = pp + p;
      pp = p;
      p = res;
    }
    return res;
  }

  private static int goStep(int i) {
    HashMap<Integer, Integer> existing = new HashMap<>();
    if (existing.containsKey(i)) {
      return existing.get(i);
    }

    if (i == 1) {
      return 1;
    }
    if (i == 2) {
      return 2;
    }
    int i1 = goStep(i - 1) + goStep(i - 2);
    existing.put(i, i1);
    return i1;
  }
}

11 排序

排序算法的执行效率:

对于排序算法的执行效率的分析,我们一般会从这几个方面来衡量:

  • 最好情况、最坏情况、平均情况时间复杂度
  • 时间复杂度的系数、常数、低阶
  • 比较次数和交换(移动次数)

排序算法的内存消耗:

原地排序:特指空间复杂度是O(1)的算法。

排序算法的稳定性:

稳定性:如果待排序的序列中存在值相等的元素,经过排序后,相等元素之间原有的先后顺序不变。

为什么要稳定:稳定排序算法可以保持第一个排序值相同的两个对象,在经过其他值第二次排序后,前后顺序不变。

冒泡排序

package sort;

/**
 * Description:冒泡排序
 *
 * @author hanshuangwen
 * @create 2021-05-29 21:16
 */
public class BubbleSort {
  public static void bubbleSort(int[] a) {
    if (a.length < 1) {
      return;
    }

    for (int i = 1; i < a.length; i++) {
      //提前退出冒泡排序的标志位
      boolean isSort = true;
      for (int j = 0; j < a.length - i; j++) {
        if (a[j + 1] < a[j]) {
          int temp = a[j + 1];
          a[j + 1] = a[j];
          a[j] = temp;
          isSort = false;
        }
      }
      if (isSort) {
        break;
      }
    }
  }

  public static void main(String[] args) {
    int[] a = {4, 5, 6, 3, 2, 1};
    bubbleSort(a);
    for (int i : a) {
      System.out.println(i);
    }
  }
}

最好情况:123456,会提前退出循环,所以是O(n)

最坏情况:654321,所以是O(n2);

是原地排序。

是稳定排序算法。

插入排序

package sort;

/**
 * Description:
 *
 * @author hanshuangwen
 * @create 2021-05-29 22:09
 */
public class InsertSort {
  public static void insertSort(int[] a, int n) {
    if (n < 1) {
      return;
    }
    for (int i = 1; i < n; i++) {
      int value = a[i];
      int j = i - 1;
      while (j >= 0) {
        if (a[j] > value) {
          a[j + 1] = a[j];
        } else {
          break;
        }
        j--;
      }
      a[j + 1] = value;
    }
  }
}

最好情况:123456,O(n)

最坏情况:654321,O(n2)

是原地排序

是稳定排序算法

选择排序

package sort;

/**
 * Description:
 *
 * @author hanshuangwen
 * @create 2021-05-29 22:38
 */
public class SelectSort {
  public static void selectSort(int[] a,int n){
    if (n<1){
      return;
    }
    for (int i = 0; i < n; i++) {
      int min=i;
      for (int j=i+1;j<n;j++){
        if (a[j]<a[min]){
          min=j;
        }
      }
      int temp=a[i];
      a[i]=a[min];
      a[min]=temp;
    }
  }

  public static void main(String[] args) {
    int[] a={4,5,6,3,2,1};
    selectSort(a,a.length);
    for (int i : a) {
      System.out.println(i);
    }
  }
}

最好情况最坏情况:O(n2)

是原地排序算法

不是稳定的排序算法

归并排序

package sort;

/**
 * Description:归并排序
 *
 * @author hanshuangwen
 * @create 2021-05-30 21:19
 */
public class MergeSort {
  public static void mergeSort(int[] a,int p,int r,int[] temp){
    if (p>=r){
      return;
    }
    int q=p+(r-p)/2;
    mergeSort(a,p,q,temp);
    mergeSort(a,q+1,r,temp);
    merge(a,p,q,r,temp);
  }

  private static void merge(int[] a, int p, int q, int r,int[] temp) {
    int i=p;
    int j=q+1;
    int k=0;
    while (i<=q&&j<=r){
      if (a[i]<=a[j]){
        temp[k++]=a[i++];
      }else {
        temp[k++]=a[j++];
      }
    }
    while (i<=q){
      temp[k++]=a[i++];
    }
    while (j<=r){
      temp[k++]=a[j++];
    }
    for (int l = 0; l < r-p+1; l++) {
      a[p+l]=temp[l];
    }
  }

  public static void main(String[] args) {
    int[] a={11,8,3,9,7,1,2,5};
    int[] temp=new int[a.length];
    mergeSort(a,0,a.length-1,temp);
    for (int i : a) {
      System.out.println(i);
    }
  }
}

递归的时间复杂度判断:一个问题a可以分解成多个子问题b、c,那求解问题a就可以分解为求解问题b,c。T(a)=T(b)+T©+K。

其中K等于将两个子问题b,c的结果合并成问题a的结果所消耗的时间。

假设对n个元素进行归并排序需要的时间是T(n),那分解成两个子数组排序的时间就是T(n/2)。merge()函数合并两个有序子数组的时间复杂度是O(n)。所以归并排序的时间复杂度的计算公式就是:
T(1)=C     n=1时,只需要常量级的执行时间,所以表示成C
T(n)=2*T(n/2)+n  n>1

最好最坏平均都是O(nlogn),是稳定排序算法。

快速排序

package sort;

/**
 * Description:
 *
 * @author hanshuangwen
 * @create 2021-05-31 22:19
 */
public class QuickSort {
  public static void quickSort(int[] a, int p, int r) {
    if (p >= r) {
      return;
    }
    //找中间比较数
    int q = findMid(a, p, r);
    quickSort(a, p, q - 1);
    quickSort(a, q + 1, r);
  }

  private static int findMid(int[] a, int p, int r) {
    int pivot = a[r];
    int i = p;
    int j = p;
    while (i <= r - 1 && j <= r - 1) {
      if (a[j] >= pivot) {
        j++;
      } else {
        int temp = a[j];
        a[j] = a[i];
        a[i] = temp;
        i++;
        j++;
      }
    }
    int temp = a[j];
    a[j] = a[i];
    a[i] = temp;
    return i;
  }

  public static void main(String[] args) {
    int[] a = {8, 10, 2, 3, 6, 1, 5};
    quickSort(a, 0, a.length - 1);
    for (int i : a) {
      System.out.println(i);
    }
  }
}

假设每次分区,都能正好把数组分成两个相同大小的小区间,并且分区的时间复杂度是O(n)那快排的时间复杂度公式如下:
T(1)=C n=1
T(n)=n+T(n/2) n>1
但是当分区不均等时,并且是递增有序时,那分区就要进行n次,所以最终快排的时间复杂度会退化成O(n2)

总结:

  1. 归并排序的处理过程时由下到上的,是稳定的O(nlogn),但不是原地排序算法。
  2. 快排的处理过程是由上到下的,不是稳定的,且可能退化成O(n2)的,但可以是原地排序算法。

如何用快排思想在O(n)内找打无序数组中第k大元素?

package sort;

/**
 * Description:在O(n)内找无序数组的第k大
 *
 * @author hanshuangwen
 * @create 2021-06-01 22:04
 */
public class FindTopK {
  public static int findTopK(int[] a, int p, int r, int k) {
    int q = findPivot(a, p, r);
    if (q + 1 == k) {
      return q;
    } else if (k > q + 1) {
      return findTopK(a, q + 1, r, k);
    } else {
      return findTopK(a, p, q - 1, k);
    }
  }

  private static int findPivot(int[] a, int p, int r) {
    int pivot = a[r];
    int i = p;
    int j = p;
    while (i <= r - 1 && j <= r - 1) {
      if (a[j] < pivot) {//注意左分区是大于中间节点的,如果是找第K小,那么右分区应该是大于中间节点的
        j++;
      } else {
        int temp = a[j];
        a[j] = a[i];
        a[i] = temp;
        i++;
        j++;
      }
    }
    int temp = a[j];
    a[j] = a[i];
    a[i] = temp;
    return i;
  }

  public static void main(String[] args) {
    int[] a = {6, 1, 3, 5, 7, 2, 4, 9, 11, 8};
    int topK = findTopK(a, 0, a.length - 1, 1);
    System.out.println(a[topK]);
  }
}

以最好的情况来看,当前每次分区都是划分成相同大小的两个区间的时候,则复杂度公式如下:
T(1)=1
T(2)=2
T(3)=4
...
T(n)=2的n-1次方
复杂度为O(n)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值