[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;
}