高频记录
排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|---|
直接插入排序 | O(n2)O(n2) | O(n2)O(n2) | O(n)O(n) | O(1)O(1) | 稳定 | 简单 |
希尔排序 | O(nlog2n)O(nlog2n) | O(n2)O(n2) | O(n)O(n) | O(1)O(1) | 不稳定 | 较复杂 |
直接选择排序 | O(n2)O(n2) | O(n2)O(n2) | O(n2)O(n2) | O(1)O(1) | 不稳定 | 简单 |
堆排序 | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | O(1)O(1) | 不稳定 | 较复杂 |
冒泡排序 | O(n2)O(n2) | O(n2)O(n2) | O(n)O(n) | O(1)O(1) | 稳定 | 简单 |
快速排序 | O(nlog2n)O(nlog2n) | O(n2)O(n2) | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | 不稳定 | 较复杂 |
归并排序 | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | O(n)O(n) | 稳定 | 较复杂 |
基数排序 | O(d(n+r))O(d(n+r)) | O(d(n+r))O(d(n+r)) | O(d(n+r))O(d(n+r)) | O(n+r)O(n+r) | 稳定 | 较复杂 |
冒泡排序(BubbleSort)
依次比较两个相邻的元素,如果顺序错误就交换,直到跑完所有元素,那么最后一个元素就是最大(最小)的值,在接着按照上述操作进行排序。
时间复杂度:O(n^2)
private void bubbleSort(int a[]){
int temp;
for(int i = 0;i<a.length-1;i++){
for(int j = 0;j<a.length-1-i;j++){
if(a[j]>a[j+1]){
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
选择排序
第一趟找出最小的放在第一个,第二趟找出第二小的放在第二个,。。。。。
时间复杂度:O(n^2)
public static void selectSort(int a[]){
for(int i = 0;i<a.length;i++){
for(int j = i+1;j<a.length;j++){
if(a[i]>a[j]){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
快速排序(QuickSort)
分治法:将一大块分解成若干小块,一块一块算
- 设置一边的值为哨兵
- 将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
- 在对剩下的区间进行排序
private void quickSort(int a[],int left,int right){
//结束条件
if(left <right){
//每一趟排序
int base = division(a,left,right);
//哨兵两边开始排序
//左边
quickSort(a,left,base-1);
//右边
quickSort(a,base+1,right);
}
}
//使用分治法排序
private int division(int a[],int left,int right){
//哨兵
int base = a[left];
while (left<right){
//哨兵在左边,所以right左移
while(left<right && a[right]>=base){
right--;
}
if(left != right) {
a[left] = a[right];
}
//哨兵在右边,所以left右移
while(left<right && a[left]<=base){
left++;
}
if(left != right) {
a[right] = a[left];
}
}
a[left] = base;
return left;
}
二分插入:
while (lo <= hi) {
//二分法一分而二,数组中间下标
final int mid = (lo + hi) >>> 1;
//二分法一分而二,数组中间下标处的值
final int midVal = array[mid];
if (midVal < value) {
/**
如果数组中间处的值比要找的值小,代表要找的值
在数组的中后部部分,所以当前下标取值为mid + 1
*/
lo = mid + 1;
} else if (midVal > value) {
/**
如果数组中间处的值比要找的值大,代表要找的值
在数组的前中部部分,所以当前下标取值为mid - 1
*/
hi = mid - 1;
} else {
//数组中间处的值与要找的值相等,直接返回数组中部的下标mid
return mid; // value found
}
}
扑克牌算法
根节点到目标节点的路径
找出最小的k个数
- 使用快速排序:O(nlogn)
public static int[] getLeastNumbers1(int[] input, int k) {
if (input == null || input.length < k) {
return null;
}
int[] output = new int[k];
pivotFindMinK(input, output, k, 0, input.length - 1);
return output;
}
private static void pivotFindMinK(int[] numbers, int[] output, int k, int start, int end) {
if (numbers == null || numbers.length == 0) {
return;
}
int left = start;
int right = end;
int temp = numbers[left];
while (left < right) {
while (numbers[right] > temp && left < right) {
right--;
}
numbers[left] = numbers[right];
while (numbers[left] <= temp && left < right) {
left++;
}
numbers[right] = numbers[left];
}
numbers[left] = temp;
if (left > k-1) {
pivotFindMinK(numbers, output, k, start, left - 1);
} else if (left < k-1) {
pivotFindMinK(numbers, output, k, right + 1, end);
} else {
System.arraycopy(numbers, 0, output, 0, k);
}
}
- 新建一个数组,放入数组前k个数,并由小到大排列好,遍历原数组,如果存在比新数组中的值更小,插入排序,而后得到新数组。
- 小根堆实现,按顺序放入小根堆---大顶堆(父节点<=子节点),小根堆大小为k,超过大小,删除堆顶元素,相反的话,加入大根堆---小顶堆(父节点>=子节点)
二分查找法
/**
* 使用递归的二分查找
*title:recursionBinarySearch
*@param arr 有序数组
*@param key 待查找关键字
*@return 找到的位置
*/
public static int recursionBinarySearch(int[] arr,int key,int low,int high){
if(key < arr[low] || key > arr[high] || low > high){
return -1;
}
int middle = (low + high) / 2; //初始中间位置
if(arr[middle] > key){
//比关键字大则关键字在左区域
return recursionBinarySearch(arr, key, low, middle - 1);
}else if(arr[middle] < key){
//比关键字小则关键字在右区域
return recursionBinarySearch(arr, key, middle + 1, high);
}else {
return middle;
}
一个整形数组里求两个数的和能不能等于一个给定数
- 如果数组是有序的,两个下标,如果相加大于给定数,左边--,如果小于给定数,右边++,直到数据相等或者右边等于左边
int[] a = {1,4,5,7,9,12,56,456};
int i = 0;
int j = a.length-1;
int sum = 65;
while(i<j){
if(a[i] + a[j] >sum){
j--;
}else if(a[i] + a[j] <sum){
i++;
}else{
System.out.println(a[i] + "+" + a[j] + "=" + sum);
break;
}
}
- 如果数组是无序的
public static void twoSum(int[] nums,int target){
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0;i<nums.length;i++){
System.out.println("--------");
if(map.containsKey(nums[i])){
System.out.println(nums[map.get(nums[i])] + "+" + nums[i] + "=" + target);
break;
}
int value = target-nums[i];
map.put(value,i);
}
}
连续子数组的最大和
public int maxSubArray(int[] nums) {
int max = nums[0];
int sum = nums[0];
for(int i = 1;i<nums.length;i++){
if(nums[i] + sum < nums[i]){
sum = nums[i];
}else{
sum +=nums[i];
}
if(sum > max){
max = sum;
}
}
return max;
}
摩尔投票法
class Solution {
public int majorityElement(int[] nums) {
int x = 0, votes = 0;
for(int num : nums){
if(votes == 0) x = num;
votes += num == x ? 1 : -1;
}
return x;
}
}
二叉树
二叉树遍历
//层序遍历
public void levelOrder(BinaryTreeNode root) {
BinaryTreeNode temp;
Queue<BinaryTreeNode> queue = new LinkedList<BinaryTreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
temp = queue.poll();
System.out.print(temp.getData() + "\t");
if (null != temp.getLeft())
queue.offer(temp.getLeft());
if (null != temp.getRight()) {
queue.offer(temp.getRight());
}
}
}
//前序遍历递归的方式
public void preOrder(BinaryTreeNode root) {
if (null != root) {
System.out.print(root.getData() + "\t");
preOrder(root.getLeft());
preOrder(root.getRight());
}
}
//前序遍历非递归的方式
public void preOrderNonRecursive(BinaryTreeNode root) {
Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
stack.push(root);
while (!stack.isEmpty()) {
BinaryTreeNode node = stack.pop();
System.out.print(node.getData() + "\t");
if (node.getRight() != null) {
stack.push(node.getRight());
}
if (node.getLeft() != null) {
stack.push(node.getLeft());
}
}
}
//中序便利非递归
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
Stack<TreeNode> stack = new Stack<>();
stack.add(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
if(node.left != null){
stack.add(node);
stack.add(node.left);
}else{
list.add(node.val);
if(node.right != null){
stack.add(node.right);
}else{
while(!stack.isEmpty()){
TreeNode node1 = stack.pop();
list.add(node1.val);
if(node1.right != null){
stack.add(node1.right);
break;
}
}
}
}
}
return list;
}
//后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
if(root == null){
return new ArrayList<>();
}
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.add(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
list.add(node.val);
if(node.left != null){
stack.add(node.left);
}
if(node.right != null){
stack.add(node.right);
}
}
List<Integer> result = new ArrayList<>();
for(int i = list.size()-1;i>=0;i--){
result.add(list.get(i));
}
return result;
}
//之字形打印
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> listList = new ArrayList<>();
if(root == null){
return listList;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int i = 1;
while(!queue.isEmpty()){
LinkedList<Integer> list = new LinkedList<>();
int count = queue.size();
for(int j = 0;j<count;j++){
TreeNode node = queue.poll();
if(i%2 == 0){
list.addFirst(node.val);
}else{
list.add(node.val);
}
if(node.left != null){
queue.add(node.left);
}
if(node.right != null){
queue.add(node.right);
}
}
i++;
if(list.size()>0){
listList.add(list);
}
}
return listList;
}
两个view第一个公共父view
- 引入set集和,一个用来存贮a数据,将b放入a数组,当数组重复会返回false,放入另一个set中做返回。
public static Set<Integer> getIds(Integer[] a, Integer[] b){
Set<Integer> same = new HashSet<Integer>(); //用来存放两个数组中相同的元素
Set<Integer> temp = new HashSet<Integer>(); //用来存放数组a中的元素
for (int i = 0; i < a.length; i++) {
temp.add(a[i]); //把数组a中的元素放到Set中,可以去除重复的元素
}
for (int j = 0; j < b.length; j++) {
//把数组b中的元素添加到temp中
//如果temp中已存在相同的元素,则temp.add(b[j])返回false
if(!temp.add(b[j]))
same.add(b[j]);
}
}
如果想知道两个数组的索引,使用map存储,key是值,value是数组索引,在进行map.containsKey()对比,找到则使用
- 将a数组放入set中,将b轮询放入set中,返回false,则重复值
public static void getIds(int a[], int b[]) {
Set<Integer> set1 = new HashSet<>();
for(Integer integer:a){
set1.add(integer);
}
for(Integer j:b){
if(!set1.add(j)){
System.out.println(j);
break;
}
}
}
- 两个链表中第一个根节点
两个指针分别跑两条链,短的跑完后,将长的头赋值过去,再开始跑,长的跑完亦然,最终两指针相交地方为两链表的第一个根节点
public static void getNode(Node nodeA, Node nodeB) {
Node pA = nodeA;
Node pB = nodeB;
while (pA != pB) {
if (pA.next == null) {
pA = nodeB;
} else {
pA = pA.next;
}
if (pB.next == null) {
pB = nodeA;
} else {
pB = pB.next;
}
}
System.out.print(pA.value);
}
- 二叉树的最近公共祖先
class Solution {
TreeNode node;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
dps(root,p,q);
return node;
}
public boolean dps(TreeNode root, TreeNode p, TreeNode q){
if(root == null){
return false;
}
if(root.val == p.val){
node = p;
return true;
}
if(root.val == q.val){
node = q;
return true;
}
boolean a = dps(root.left,p,q);
boolean b = dps(root.right,p,q);
if(a && b){
node = root;
}
if(a || b){
return true;
}
return false;
}
}
判断链表是不是环链,并返回入口
两个指针,一个一次跑一个,一个一次跑两个,相交的话就是存在环链
将跑的慢的从头开始一个一个跑,跑的快的从相交的点开始跑,一个一个跑,最终相遇的就是入口
public static Node isCircle(Node node) {
Node p = node;
Node q = node;
while (p != null && q != null) {
p = p.next.next;
q = q.next;
if(p == q){
while(node != p){
node = node.next;
p = p.next;
}
return node;
}
}
return p;
}
有一个整型数组,包含正数和负数,将负数放在左边,且保证相对位置保持不变
转化成链表,遍历链表将复苏和单独成链后连接到剩下的正数链上
数组两个索引,一个索引记录负数的尾部,一个所以负责遍历,每找到一个负数,将其插入该值,后续值后移一位。
public static void get(int a[]){
for(int i = 0,j = 0;j<a.length;j++){
if(a[j]<0){
int temp = a[j];
int k = j;
while(i<=k-1){
a[k] = a[k-1];
k--;
}
a[i] = temp;
i++;
}
}
for(int i:a){
System.out.println(i);
}
}
打印蛇形矩阵
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length == 0) return new int[0];
int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1, x = 0;
int[] res = new int[(r + 1) * (b + 1)];
while(true) {
for(int i = l; i <= r; i++) res[x++] = matrix[t][i]; // left to right.
if(++t > b) break;
for(int i = t; i <= b; i++) res[x++] = matrix[i][r]; // top to bottom.
if(l > --r) break;
for(int i = r; i >= l; i--) res[x++] = matrix[b][i]; // right to left.
if(t > --b) break;
for(int i = b; i >= t; i--) res[x++] = matrix[i][l]; // bottom to top.
if(++l > r) break;
}
return res;
}
}
求二叉树的叶子节点数
public int testTree(Tree tree){
if(tree==null){
return 0;
}
if(tree.left==null&&tree.right==null){
System.out.println("叶子节点:"+tree.val);
return 1;
}
return testTree(tree.left)+testTree(tree.right);
}
求二叉树的深度
public static int treeDepth(TreeNode root) {
if (root == null) {
return 0;
}
// 计算左子树的深度
int left = treeDepth(root.left);
// 计算右子树的深度
int right = treeDepth(root.right);
// 树root的深度=路径最长的子树深度 + 1
return left >= right ? (left + 1) : (right + 1);
}
动态规划
数组最大和
累加值:一个一个累加的值
最大值:记录累加值的最大值
假设数组为最大,每次添加一个值,如果i-1的值<0,那么把i放入累加值,如果>0,那么将i的值加到累加值,如果累加值>最大值,则更新最大值。
如果需要数组的最大和的下标,对最大值的下标进行记录
public static void main(String[] args) {
int[] array = {1,-2,3,10,-4,7,2,-5};
Long begintime = System.nanoTime();
int result = FindGreatestSumOfSubArray(array);
Long endtime = System.nanoTime();
System.out.println("连续子数组的最大和为:"+result+",运行时间:"+(endtime - begintime) + "ns");
}
public static int FindGreatestSumOfSubArray(int[] array) {
int len = array.length;
if (len == 0){
return 0;
}
int[] currentsum = new int[len];
currentsum[0] = array[0];
int greatsetsum = array[0];
System.out.println("第1步:累加子数组和:"+currentsum[0]+",最大子数组和:"+greatsetsum);
for(int i=1;i<array.length;i++){
//下面是动态规划的状态转移方程
if(currentsum[i-1]>0){
currentsum[i] = currentsum[i-1] + array[i];
}else{
currentsum[i] = array[i];
}
//根据currentsum的值更新greatsetsum的值
if(currentsum[i] > greatsetsum){
greatsetsum = currentsum[i];
}
System.out.println("第"+(i+1)+"步:累加子数组和:"+currentsum[i]+",最大子数组和:"+greatsetsum);
}
return greatsetsum;
}
}