Math.random()方法等概率返回[0,1)上的随机数
public static int[] randomArray(int maxLen, int maxValue){
int len = (int)(Math.random()*maxLen);
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int)(Math.random()*maxValue);
}
return arr;
}
验证上千或上万次:
左移乘2,右移除2
异或:相同为0,不同为1(无进位相加)
同或:相同为1,不同为0
例:0011^1011=1000
性质:
前提:两个数内存不同,否则就是自己异或自己
a = a^b;//a=a^b b=b
b = a^b;//a=a^b b=a^b^b=a
a = a^b;//a=a^b^a=b b=a
返回一个除最右侧的1,其余都是0的数:
N = N&((~N)+1)//取反+1后,除最右边的1之外,其余位置都相反或是0
数组中只有一种数出现奇数次,其他数都是偶数次:
所有数字都异或得到的结果就是唯一奇数次的数字
数组中有2种数出现奇数次,其他数都是偶数次:
eor=a^b
,且eor≠0,则eor的最右侧1必然是a和b一个是1另一个不是1的位置rightone=eor&((~eor)+1)
,取eor的最右侧1eor2^eor
——bint count=0;
whilt(N!=0){
int rightone = N&((~N)+1);//提取最右侧1
count++;
N^=rightone;//抹去最右侧1
}
return count;
最高位为符号位,其余31位正数是本身,负数的实际值是位值取反+1
负数取反+1是为了统一位运算操作,正负数加法均用相同的加法操作
for(int i=31;i>=0;i--){
System.out.print( (num & (1<<i) ) == 0 ? 0 : 1);
}
取反+1
Integer.MIN_VALUE和0取反+1后还是它自己
a = ~b+1
public static int binaryFind(int[] arr, int value){
if(arr.length==0||arr==null)
return -1;
int L=0,R=arr.length-1;
while(L<=R){
int mid = (L+R)/2;
if(arr[mid]==value)
return mid;
else if(arr[mid]>value)
R = mid-1;
else
L=mid+1;
}
return -1;
}
public static int find(int[] arr, int value){
if(arr.length==0||arr==null)
return -1;
int L=0,R=arr.length-1,ans=-1;
while(L<=R){
int mid = (L+R)/2;
if(arr[mid]>=value){
ans = mid;
R = mid-1;
}else {
L = mid+1;
}
}
return ans;
}
与上题大致相同
**注意:**不一定是有序数组才能用二分法,只要能够排除另半边的情况就行
==mid的计算:==如果希望mid-1和mid+1都在数组长度范围内,使用mid=(L+R)/2会导致在取mid+1或mid-1时可能会出现越界,因此while退出循环的条件为L
public static int findLocalMin(int[] arr){
if(arr==null||arr.length==0)
return -1;
int N = arr.length;
if(N==1||arr[1]>arr[0])
return 0;
if(arr[N-1]<arr[N-2])
return N-1;
int L=0,R=N-1;
while(L<R-1){
int mid=(L+R)/2;
if(arr[mid]<arr[mid-1]&&arr[mid]<arr[mid+1])
return mid;
else if(arr[mid]>arr[mid-1]){
R=mid-1;
}else {
L=mid+1;
}
}
return arr[L]<arr[R]?L:R;
}
public static void selectsort(int[] arr){
if(arr==null||arr.length<2)//先考虑边界条件
return ;
int N = arr.length;
for(int i=0;i<N;++i){
int minindex=i;//记录最小值下标即可
for(int j=i+1;j<N;++j){
minindex = arr[j]<arr[minindex]?j:minindex;
}
int tmp = arr[minindex];//交换过程可以另外写成一个函数
arr[minindex] = arr[i];
arr[i] = tmp;
}
}
public static void bubblesort(int[] arr){
if(arr==null||arr.length<2)
return ;
int N=arr.length;
for (int i=N-1;i>=0;--i){
for(int j=0;j<i-1;++j){
if(arr[j]>arr[j+1])
swap(arr, j, j+1);
}
}
}
public static void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void insertsort(int[] arr){
if(arr==null||arr.length<2)
return ;
int N=arr.length;
for(int i=1;i<N;++i){
int idx = i;
while((idx-1>=0)&&arr[idx-1]>arr[idx]){
swap(arr, idx-1, idx);
idx--;
}
}
}
public static void insertsort2(int[] arr){
if(arr==null||arr.length<2)
return ;
int N=arr.length;
for(int i=1;i<N;++i){
for(int j=i-1;j>=0&&arr[j]>arr[j+1];--j)
swap(arr, j, j+1);
}
}
public static void mergeSort(int[] arr){
if(arr==null||arr.length<2)
return;
merge(arr,0,arr.length-1);
}
public static void merge(int[] arr, int l, int r) {
if(l==r)
return;
int mid=l+((r-l)>>1);
merge(arr,l,mid);
merge(arr,mid+1,r);
mergeAll(arr,l,mid,r);
}
public static void mergeAll(int[] arr, int l, int mid, int r) {
int[] tmp = new int[r-l+1];
int i=0,p=l,q=mid+1;
while(p<=mid&&q<=r){
tmp[i++]=arr[p]<=arr[q]?arr[p++]:arr[q++];
}
while (p<=mid)
tmp[i++]=arr[p++];
while (q<=r)
tmp[i++]=arr[q++];
for (int j = 0; j < tmp.length; j++) {
arr[l+j] = tmp[j];
}
}
//非递归实现
public static void mergeSort2(int[] arr){
if(arr==null||arr.length<2)
return;
int N=arr.length,mergesize=1;
while (mergesize<N){
int l=0;
while (l<N){//分组合并
int mid=l+mergesize-1;
if(mid>=N)
break;
int r=Math.min(mid+mergesize, N-1);
mergeAll(arr,l,mid,r);
l=r+1;
}
if(mergesize>N/2)//防止mergesize*2后溢出
break;
mergesize<<=1;
}
}
在一个数组中,数左边比它小的数的总和叫做数的小和,数组中所有数的小和累加起来叫做数组小和
使用归并排序,每次mergeAll的时候产生小和:
左半边当前数a<右半边当前数b时,产生小和,数量为右半边大于a的数的个数
将上述过程中产生的所有小和累加,即为数组的小和
通过归并排序将时间复杂度降到O(N*logN),避免浪费大量的比较
public static int LessSum(int[] arr){
if(arr==null||arr.length<2)
return 0;
return mergeLessSum(arr,0,arr.length-1);
}
public static int mergeLessSum(int[] arr, int l, int r) {
if(l==r)
return 0;
int mid=l+((r-l)>>1);
return mergeLessSum(arr,l,mid)+mergeLessSum(arr,mid+1,r)+mergeAllSum(arr,l,mid,r);
}
public static int mergeAllSum(int[] arr, int l, int mid, int r) {
int[] help = new int[r-l+1];
int i=0,p=l,q=mid+1;
int res=0;
while(p<=mid&&q<=r){
res+=arr[p]<arr[q]?(r-q+1)*arr[p]:0;//计算当前arr[q]的小和
help[i++]=arr[p]<arr[q]?arr[p++]:arr[q++];//这里只能是<,不是<=,因为需要保留左半边的较小数
}
while(p<=mid)
help[i++] = arr[p++];
while(q<=r)
help[i++] = arr[q++];
for (int j = 0; j < help.length; j++) {
arr[l+j] = help[j];
}
return res;
}
public static void main(String[] args) {
int res = LessSum(new int[]{1,5,2,6,3,7,4});
System.out.println(res);
}
使用归并排序:
若左半边当前数a>右半边当前数b,则记录a及其以后的数的个数作为b的当前降序对数
i从0开始遍历,j=-1记录<=a的区域边界:
k从0开始遍历,i=-1、j=arr.length记录小于区和大于区的边界:
# 荷兰国旗
public static int[] netherlandsFlag(int[] arr, int l, int r){
if(l>r)
return new int[]{-1,-1};
if(l==r)
return new int[]{l,r};
int less=l-1,more=r,index=l;
while(index<more){
if(arr[index]==arr[r])
index++;
else if(arr[index]<arr[r]){
swap(arr, index++, ++less);
}else{
swap(arr, index, --more);
}
}
swap(arr,more,r);
return new int[]{less+1,more};
}
#快速排序
public static void sort(int[] arr, int l, int r){
if(l>=r)
return;
swap(arr, l+(int)Math.random()*(r-l+1), r);
int[] equalArea = netherlandsFlag(arr, l, r);
sort(arr, l, equalArea[0]-1);
sort(arr, equalArea[1]+1, r);
}
一般要求数组是整数,范围较窄
遍历数组,数字对应位置计数+1;最后根据计数情况打印相应个数的数字
例如:{1,2,3,4,2,2,3,0,2,1,4,2,5,6,2,0,3,6,5,3,5,6}
计数数组结果为[2,2,6,4,2,3,3],排序结果为[0,0,1,1,2,2,2,2,2,2,3,3,3,3,4,4,5,5,5,6,6,6]
一般要求数组是十进制正整数
例如:{123,9,234,32,15,24,35,47,332}
准备十个桶(队列),根据个位数字进桶:[[32,332],[123],[234,24],[15,35],[47],[9]]
依次出桶:{32,332,123,234,24,15,35,47,9}
根据十位数字进桶:[[9],[15],[123,24],[32,332,234,35],[47]]
依次出桶:{9,15,123,24,32,332,234,35,47}
根据百位数进桶:[[9,15,24,32,35,47],[123],[234],[332]]
依次出桶:{9,15,24,32,35,47,123,234,332}
public class radixSort {
public static void sort(int[] arr){
if(arr==null||arr.length<2)
return;
radixSort(arr,0,arr.length,maxbits(arr));
}
private static int maxbits(int[] arr) {
int max = Integer.MIN_VALUE;
for(int i=0;i<arr.length;++i){
max = Math.max(max, arr[i]);
}
int ans=0;
while(max!=0){
ans++;
max/=10;
}
return ans;
}
public static void radixSort(int[] arr, int l, int r, int digit){
final int radix=10;
int i=0,j=0;
int[] help = new int[r-l];
for (int d = 1; d <= digit; d++) {
int[] count = new int[radix];
for(i=l;i<r;++i){
j = getDigit(arr[i], d);
count[j]++;
}
for(i=0;i<radix-1;++i){
count[i+1]+=count[i];
}
for(i=r-1;i>=l;--i){
j = getDigit(arr[i], d);
help[count[j]-1] = arr[i];
count[j]--;
}
for(i=l,j=0;i<r;++i,++j)
arr[i] = help[j];
}
}
private static int getDigit(int num, int d) {
while(--d>0){
num/=10;
}
return num%10;
}
public static void main(String[] args) {
int[] a = new int[]{2,32,14,23,245,324,11,24,21,71};
sort(a);
for (int i : a) {
System.out.print(i+" ");
}
}
}
对非基础类型来说有重要意义,对基础类型来说稳定性没有意义
有的排序算法可以实现成稳定的,有的不行
算法 | 时间复杂度 | 额外空间复杂度 | 稳定性 |
---|---|---|---|
选择排序 | O(N2) | O(1) | 无 |
冒泡排序 | O(N2) | O(1) | 有 |
插入排序 | O(N2) | O(1) | 有 |
归并排序 | O(N*logN) | O(N) | 有 |
随机快排 | O(N*logN) | O(logN) | 无 |
堆排序 | O(N*logN) | O(1) | 无 |
计数排序 | O(N) | O(M) | 有 |
基数排序 | O(N) | O(N) | 有 |
系统提供的排序方法先通过反射判断数据类型,若是基础类型,则采用快排,若是引用类型,为了保持稳定性,采用归并排序
堆结构是用数组实现的完全二叉树,第i个节点的左孩子下标为2i+1,右孩子下标为2i+2,父节点下标为(i+1)/2;若数组0位置弃用,则节点i的左孩子为2i,右孩子为2i+1,父节点为i/2,所有计算可以用位运算实现
特殊的堆有大根堆和小根堆
# 大根堆
public class Heap {
private int heapsize;
private int[] heap;
private int MAX_LEN=100;
public Heap() {
heapsize = 0;
heap=new int[MAX_LEN];
}
public void push(int value){
if(heapsize==MAX_LEN)
throw new IndexOutOfBoundsException("heap full");
heap[heapsize] = value;
heapInsert(heap, heapsize++);
}
public int pop(){
if(heapsize==0)
throw new IndexOutOfBoundsException("heap empty");
int ans = heap[0];
swap(heap, 0, --heapsize);
heapify(heap, 0, heapsize);
return ans;
}
private void heapify(int[] heap, int i, int heapsize) {
int left=2*i+1;
while(left<heapsize){
int largest = left+1<heapsize&&heap[left+1]>heap[left]?left+1:left;
largest = heap[largest]>heap[i]?largest:i;
if(largest==i)
break;
swap(heap, i, largest);
i = largest;
left = 2*i+1;
}
}
private void heapInsert(int[] heap, int i) {
while(heap[i]>heap[(i-1)/2]){
swap(heap, i, (i-1)/2);
i = (i-1)/2;
}
}
private void swap(int[] heap, int i, int i1) {
int tmp = heap[i];
heap[i] = heap[i1];
heap[i1] = tmp;
}
}
给定几乎排好序的数组,每个数字移动不超过k步即可有序:
对前k个数调整为小根堆,每输出一个数,加入后一个数进行堆的调整
实质是重载比较运算符
系统提供的堆PriorityQueue默认是小根堆,需要写一个Comparable接口的实现类比较器传入PriorityQueue,更改为大根堆
public class PrefixTree {
public static class Node{
public int pass;
public int end;
public Node[] nexts;
public Node(){
pass=0;
end=0;
nexts=new Node[26];
}
}
public static class prefixTree{
private Node root;
public prefixTree(){
root = new Node();
}
public void insert(String word){
if(word==null)
return;
char[] chs = word.toCharArray();
Node node = root;
node.pass++;
int index=0;
for (int i = 0; i < chs.length; i++) {
index = chs[i]-'a';
if(node.nexts[index]==null)
node.nexts[index] = new Node();
node = node.nexts[index];
node.pass++;
}
node.end++;
}
public int countStr(String word){
if(word==null)
return 0;
char[] chs=word.toCharArray();
Node node = root;
int index=0;
for (int i = 0; i < chs.length; i++) {
index = chs[i]-'a';
if(node.nexts[index]==null)
return 0;
node = node.nexts[index];
}
return node.end;
}
public int countPrefix(String prefix){
if(prefix==null)
return 0;
char[] chs=prefix.toCharArray();
Node node = root;
int index=0;
for (int i = 0; i < chs.length; i++) {
index = chs[i]-'a';
if(node.nexts[index]==null)
return 0;
node = node.nexts[index];
}
return node.pass;
}
public void delete(String word){
if(countStr(word)!=0){
char[] chs=word.toCharArray();
Node node = root;
node.pass--;
int index=0;
for (int i = 0; i < chs.length; i++) {
index = chs[i]-'a';
if(--node.nexts[index].pass==0) {
node.nexts[index]=null;
return;
}
node = node.nexts[index];
}
node.end--;
}
}
}
}
笔试时优先考虑时间复杂度
输入链表头节点,奇数长度返回中点,偶数长度返回上中点
public static Node midOrUpMid(Node head){
if(head==null||head.next==null||head.next.next==null)
return head;
Node slow = head.next;
Node fast = head.next.next;
while(fast.next!=null&&fast.next.next!=null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
输入链表头节点,奇数长度返回中点,偶数长度返回下中点
public static Node midOrDownMid(Node head){
if(head==null||head.next==null)
return head;
Node slow = head.next;
Node fast = head.next;
while(fast.next!=null&&fast.next.next!=null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个
public static Node midOrUpMidPre(Node head){
if(head==null||head.next==null||head.next.next==null)
return null;
Node slow = head;
Node fast = head.next.next;
while(fast.next!=null&&fast.next.next!=null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个
public static Node midOrDownMidPre(Node head){
if(head==null||head.next==null)
return null;
if(head.next.next==null)
return head;
Node slow = head;
Node fast = head.next;
while(fast.next!=null&&fast.next.next!=null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
//利用栈
public static boolean isPalindrome(Node head){
if(head==null||head.next==null)
return true;
Stack<Node> stack = new Stack<>();
Node slow = head, fast=head.next;
while(fast.next!=null&&fast.next.next!=null){
slow = slow.next;
fast = fast.next.next;
}
slow = slow.next;
while(slow!=null){
stack.push(slow);
slow = slow.next;
}
while(stack.peek()!=null){
if(head.val!=stack.pop().val)
return false;
head = head.next;
}
return true;
}
//空间复杂度为O(1) 修改后半部分指向
public static boolean isPalindrome2(Node head){
if(head==null||head.next==null)
return true;
Node n1=head,n2=head;
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;
boolean ans = true;
while(n2!=null&&n1!=null){
if(n1.val!=n2.val){
ans = 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 ans;
}
对链表进行荷兰国旗划分
public static Node partition(Node head, int pivot){
Node lessHead=null, lessTail=null;
Node equalHead=null, equalTail=null;
Node moreHead=null, moreTail=null;
Node next;
while(head!=null){
next = head.next;
head.next=null;
if(head.val<pivot){
if(lessHead==null){
lessHead=head;
lessTail=head;
}else {
lessTail.next=head;
lessTail=head;
}
}else if(head.val==pivot){
if(equalHead==null){
equalHead=head;
equalTail=head;
}else{
equalTail.next=head;
equalTail=head;
}
}else{
if(moreHead==null){
moreHead=head;
moreTail=head;
}else{
moreTail.next=head;
moreTail=head;
}
}
head = next;
}
if(lessTail!=null){
lessTail.next=equalHead;
equalTail = equalTail==null?lessTail:equalTail;
}
if(equalTail!=null){
equalTail.next = moreHead;
}
return lessHead!=null?lessHead:(equalHead!=null?equalHead:moreHead);
}
//方法一:使用哈希表存放老节点和新节点,
//第一次遍历新建所有新节点;
//第二次遍历按照老节点的指向修改新节点的next和rand指针
//方法二:第一次遍历,新建所有新节点并放置于每两个老节点之间;
//第二次遍历,每次取出新老节点,新节点的next修改为老节点的next的next,rand修改为老节点的rand的next;
//第三次遍历,将新老节点在next方向上分离出来
public class LinkCopy {
public static class Node{
int val;
Node next;
Node rand;
public Node(int val){
this.val=val;
}
}
public Node copy(Node head){
if(head==null)
return null;
Node cur = head, next = null;
//第一次遍历,插入新节点
while(cur!=null){
next = cur.next;
cur.next = new Node(cur.val);
cur.next.next = next;
cur = next;
}
cur = head;
Node curCpoy = null;
//第二次遍历,修改rand指针
while(cur!=null){
next = cur.next.next;
curCpoy = cur.next;
curCpoy.rand = cur.rand!=null?cur.rand.next:null;
cur = next;
}
Node newHead = head.next;
cur = head;
//第三次遍历,分离新老链表
while (cur!=null){
next = cur.next.next;
curCpoy = cur.next;
cur.next = next;
curCpoy.next = next!=null?next.next:null;
cur = next;
}
return newHead;
}
}
//找到链表中入环的节点,
//方法一:HashSet记录节点,第一个重复的节点即为入环处
//方法二:快慢指针分别每次走一步和两步,相遇后,fast指针从头开始,与slow一起每次走一步,最后会相遇在入环处
public Node getLoopNode(Node head){ if(head==null||head.next==null||head.next.next==null){
return null;
}
Node slow = head.next, fast = head.next.next;
while(slow!=fast){
if(fast.next==null||fast.next.next==null)
return null;
slow = slow.next;
fast = fast.next.next;
}
fast = head;
while(fast!=slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
//通过getLoopNode方法获得两个入环结点;
//情况1. 两个无环链表求交点:哈希表法;长度差法
public Node getNoLoopCrossNode(Node head1, Node head2){
if(head1==null||head2==null){
return null;
}
Node cur1 = head1, cur2 = head2;
int n=0;
while(cur1.next!=null){
cur1 = cur1.next;
n++;
}
while(cur2.next!=null){
cur2 = cur2.next;
n--;
}
if(cur1!=cur2)
return null;
cur1 = n>0?head1:head2;
cur2 = cur1==head1?head2:head1;
n = Math.abs(n);
while(n!=0){
n--;
cur1 = cur1.next;
}
while (cur1!=cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}
//情况2. 一个有环一个无环链表求交点:不可能有交点
//情况3. 两个都有环的链表求交点:
//可能不相交;可能在同一个结点入环;可能在不同结点入环
public Node getLoopCrossNode(Node head1, Node loop1, Node head2, Node loop2){
Node cur1=null, cur2=null;
if(loop1==loop2){
cur1=head1;
cur2=head2;
int n=0;
while(cur1!=loop1){
n++;
cur1 = cur1.next;
}
while (cur2!=loop2){
n--;
cur2 = cur2.next;
}
cur1 = n>0?head1:head2;
cur2 = cur1==head1?head2:head1;
n = Math.abs(n);
while(n!=0){
n--;
cur1 = cur1.next;
}
while (cur1!=cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}else{
cur1 = loop1.next;
while (cur1!=loop1){
if(cur1==loop2)
return loop1;
cur1 = cur1.next;
}
return null;
}
}
不给头节点,只给想删除的节点就将其从单链表中删除
不可行方法:将下一个节点的值赋给要删除的节点,再删除下一个节点;
该方法不适用节点内容多或删除最后一个节点的情况
递归序会三次访问一个结点,在第一次进行处理即为先序,在第二次进行处理即为中序,在第三次进行处理即为后序;
所有递归都可以通过栈转化为非递归
先序:头左右->头右左->左右头,即为后序
public void postPrint2(BTNode root){
if(root!=null){
Stack<BTNode> s1 = new Stack<>();
Stack<BTNode> s2 = new Stack<>();
s1.push(root);
while(!s1.isEmpty()){
root = s1.pop();
s2.push(root);
if(root.left!=null)
s1.push(root.left);
if(root.right!=null)
s1.push(root.right);
}
while (!s2.isEmpty()){
System.out.println(s2.pop().val);
}
}
}
public void postPrint3(BTNode root){
if(root!=null){
Stack<BTNode> stack = new Stack<>();
stack.push(root);
BTNode c=null;
while(!stack.isEmpty()){
c = stack.peek();
if(c.left!=null&&root!=c.left&&root!=c.right){//左右子树均未处理
stack.push(c.left);
}else if(c.right!=null&&root!=c.right){//右子树未处理
stack.push(c.right);
}else{//左右子树处理完
System.out.println(stack.pop().val);
root = c;//root记录上一次打印的结点
}
}
}
}
利用队列
public void levelPrint(BTNode root){
if(root==null)
return;
Queue<BTNode> q = new LinkedList<>();
q.add(root);
while(!q.isEmpty()){
BTNode cur = q.poll();
System.out.println(cur.val);
if(cur.left!=null)
q.add(cur.left);
if(cur.right!=null)
q.add(cur.right);
}
}
public int maxWidth(BTNode root){
if(root==null)
return 0;
Queue<BTNode> q = new LinkedList<>();
q.add(root);
BTNode curEnd=root;//当前层最右结点
BTNode nextEnd = null;//下一层最右结点
int max=0, curLevelNodes=0;
while(!q.isEmpty()){
BTNode cur = q.poll();
if(cur.left!=null){
q.add(cur.left);
nextEnd = cur.left;
}
if(cur.right!=null){
q.add(cur.right);
nextEnd = cur.right;
}
curLevelNodes++;
if(cur==curEnd){
max = Math.max(max, curLevelNodes);
curLevelNodes=0;
curEnd=nextEnd;
}
}
return max;
}
public Queue<Integer> preSerialize(BTNode root){
Queue<Integer> q = new LinkedList<>();
preS(root, q);
return q;
}
public void preS(BTNode root, Queue<Integer> q) {
if(root==null)
q.add(null);
else{
q.add(root.val);
preS(root.left, q);
preS(root.right, q);
}
}
public Queue<Integer> levelSerialize(BTNode root){
Queue<Integer> ans = new LinkedList<>();
if(root==null)
ans.add(null);
else{
ans.add(root.val);
Queue<BTNode> q = new LinkedList<>();
q.add(root);
while(!q.isEmpty()){
root = q.poll();
if(root.left!=null){
ans.add(root.left.val);
q.add(root.left);
}else {
ans.add(null);
}
if(root.right!=null){
ans.add(root.right.val);
q.add(root.right);
}else {
ans.add(null);
}
}
}
return ans;
}
public BTNode preQueue2BTree(Queue<Integer> prelist){
if(prelist==null||prelist.size()==0)
return null;
return preBuild(prelist);
}
public BTNode preBuild(Queue<Integer> prelist) {
Integer val = prelist.poll();
if(val==null){
return null;
}
BTNode root = new BTNode(val);
root.left = preBuild(prelist);
root.right = preBuild(prelist);
return root;
}
public BTNode levelSerial2BTree(Queue<Integer> levellist){
if(levellist==null||levellist.size()==0)
return null;
BTNode root = generateNode(levellist.poll());
Queue<BTNode> q = new LinkedList<>();
if(root!=null)
q.add(root);
BTNode node = null;
while (!q.isEmpty()){
node = q.poll();
node.left = generateNode(levellist.poll());
node.right = generateNode(levellist.poll());
if(node.left!=null)
q.add(node.left);
if(node.right!=null)
q.add(node.right);
}
return root;
}
public BTNode generateNode(Integer val){
if(val==null)
return null;
return new BTNode(val);
}
public void printInOrder(BTNode root, int height, String to, int len) {
if(root==null)
return;
printInOrder(root.right, height+1, "v", len);
String val = to+root.val+to;
int lenM = val.length();
int lenL = (len-lenM)/2;
int lenR = len-lenM-lenL;
val = getSpace(lenL)+val+getSpace(lenR);
System.out.println(getSpace(height*len)+val);
printInOrder(root.left, height+1, "^", len);
}
public String getSpace(int len) {
String space=" ";
StringBuffer sb = new StringBuffer("");
for(int i=0;i<len;++i){
sb.append(space);
}
return sb.toString();
}
public class TNode {
int val;
TNode left;
TNode right;
TNode parent;
}
给定树中某个结点,返回该节点的后继结点
中序遍历序列的后继:
若结点有右子树,则后继结点为右子树的最左结点;
若结点没有右子树,则往上找直至该结点为其父节点的左孩子
public TNode getNextNode(TNode root){
if(root==null)
return null;
if(root.right!=null){
return getMostLeft(root.right);
}else{
TNode parent = root.parent;
while(parent!=null&&root!=parent.left){
root=parent;
parent=root.parent;
}
return parent;
}
}
public TNode getMostLeft(TNode root) {
if(root==null)
return null;
while(root.left!=null)
root=root.left;
return root;
}
一张长条纸上下对折后出现凹折痕,再对折后在原来折痕上下分别出现凹折痕和凸折痕,以此类推,打印所有折痕朝向(即为以此构建出的二叉树的中序遍历)
public void printFolds(int n){
print(1, n, true);
}
public void print(int i, int n, boolean down) {
if(i>n)
return;
print(i+1, n, true);
System.out.print(down?"凹":"凸");
print(i+1, n, false);
}
public class Info{
boolean isbalance;
int height;
public Info(boolean b, int h){
isbalance=b;
height=h;
}
}
public Info isBalance(BTNode root){
if(root==null)
return new Info(true, 0);
Info left = isBalance(root.left);
Info right = isBalance(root.right);
int height = Math.max(left.height, right.height)+1;
if(!left.isbalance||!right.isbalance||Math.abs(left.height-right.height)>1)
return new Info(false, height);
else
return new Info(true, height);
}
max{左子树的最大距离,右子树的最大距离,左子树高度+右子树高度+1}
public int[] maxDistance(BTNode root){
if(root==null)
return new int[]{0, 0};
int[] left = maxDistance(root.left);
int[] right = maxDistance(root.right);
int height = Math.max(left[1],right[1])+1;
int distance = Math.max(Math.max(left[0],right[0]),left[1]+right[1]+1);
return new int[]{distance, height};
}
public class searchInfo{
boolean isBST;
int maxSubBSTSize;
int max;
int min;
public searchInfo(boolean isBST, int maxSubBSTSize, int max, int min) {
this.isBST = isBST;
this.maxSubBSTSize = maxSubBSTSize;
this.max = max;
this.min = min;
}
}
public searchInfo maxBST(BTNode root){
if(root==null)
return null;
searchInfo left = maxBST(root.left);
searchInfo right = maxBST(root.right);
int min=root.val, max=root.val, maxSubBSTSize=0;
boolean isBST=false;
if(left!=null){
min = Math.min(min, left.min);
max = Math.max(max, left.max);
}
if(right!=null){
min = Math.min(min, right.min);
max = Math.max(max, right.max);
}
if((left==null?true:left.isBST)&&(right==null?true:right.isBST)&&
(left==null?true:left.max<root.val)&&(right==null?true:right.min>root.val)){
maxSubBSTSize = (left==null?0: left.maxSubBSTSize)+
(right==null?0:right.maxSubBSTSize)+1;
isBST=true;
}
return new searchInfo(isBST, maxSubBSTSize, max, min);
}
小虎去买苹果,商店只提供两种类型的塑料袋,每种类型都有任意数量:能装下6个苹果的袋子和能装下8个苹果的袋子。小虎可以自由使用两种袋子来装苹果,但是小虎有强迫症,他要求自己使用的袋子数量必须最少,且使用的每个袋子必须装满。给定一个正整数N,返回至少使用多少袋子。如果N无法让使用的每个袋子必须装满,返回-1
return apple==0?0:(apple==6||apple==8)?1:(apple==12||apple==14||apple==16)?2:-1;
return (apple-18)/8+3
给定一个正整数N,表示有N份青草统一堆放在仓库里有一只牛和一只羊,牛先吃,羊后吃,它俩轮流吃草不管是牛还是羊,每一轮能吃的草量必须是:1,4,16,64…(4的某次方),谁最先把草吃完,谁获胜。假设牛和羊都绝顶聪明都想赢,都会做出理性的决定根据唯一的参数N,返回谁会赢
public String winner(int n){
// 0 1 2 3 4
//后 先 后 先 先,谁先面临0份草谁输,谁先吃完谁赢
if(n<5)
return (n==0||n==2)?"后手":"先手";
int base=1;
while(base<=n){
if(winner(n-base).equals("后手"))//在下一局中先手变成了后手
return "先手";
if(base>n/4)//防止base*4溢出
break;
base*=4;
}
return "后手";
}
打印结果发现规律为“后先后先先”,代码可以转化为O(1)的代码
return (n%5==0||n%5==2)?"后手":"先手";
定义一种数,可以表示成若干(数量>1)连续正数和的数。比如:5 =2+3,5就是这样的数;12 = 3+4+5,12就是这样的数;1不是这样的数,因为要求数量大于1个;2 =1 + 1,2也不是,因为等号右边不是连续正数。给定一个参数N,返回是不是可以表示成若干连续正数和的数
暴力两个for循环能判断结果,打印结果后发现规律:1和2的时候是false,其余时候若n为2的次幂,返回false,否则返回true
if(num<3)
return false;
return (num&(num-1))!=0;//num&(num-1)==0,则num为2的次幂
[1 2 3 4 5 6]
[7 8 9 10 11 12]
[13 14 15 16 17 18]打印为[1 2 7 13 8 3 4 9 14 15 10 5 6 11 16 17 12 18]
public static void printZigZag(int[][] matrix){
int Ax=0,Ay=0,Bx=0,By=0;
int endrow=matrix.length-1,endcol=matrix[0].length-1;
boolean up2down=false;
while(Ax!=endrow+1){
printlevel(matrix, Ax, Ay, Bx, By, up2down);
Ax = Ay==endcol?Ax+1:Ax;
Ay = Ay==endcol?Ay:Ay+1;
By = Bx==endrow?By+1:By;
Bx = Bx==endrow?Bx:Bx+1;
up2down=!up2down;
}
System.out.println();
}
private static void printlevel(int[][] matrix, int Ax, int Ay, int Bx, int By, boolean up2down) {
if(up2down){
while(Ax!=Bx+1){
System.out.print(matrix[Ax++][Ay--]+" ");
}
}else{
while(Bx!=Ax-1){
System.out.print(matrix[Bx--][By++]+" ");
}
}
}
[1 2 3 4 5 6]
[7 8 9 10 11 12]
[13 14 15 16 17 18]打印为[1 2 3 4 5 6 12 18 17 16 15 14 13 7 8 9 10 11]
public static void printEdge(int[][] matrix, int Ax, int Ay, int Bx, int By){
if(Ax==Bx){
for(int i=Ay;i<=By;i++){
System.out.print(matrix[Ax][i]+" ");
}
}else if(Ay==By){
for(int i=Ax;i<=Bx;i++){
System.out.print(matrix[i][Ay]+" ");
}
}else{
int curX=Ax, curY=Ay;
while(curY!=By){
System.out.print(matrix[curX][curY]+" ");
curY++;
}
while (curX!=Bx){
System.out.print(matrix[curX][curY]+" ");
curX++;
}
while(curY!=Ay){
System.out.print(matrix[curX][curY]+" ");
curY--;
}
while (curX!=Ax){
System.out.print(matrix[curX][curY]+" ");
curX--;
}
}
}
public static void printRound(int[][] matrix){
int Ax=0,Ay=0,Bx= matrix.length-1,By=matrix[0].length-1;
while(Ax<=Bx&&Ay<=By){
printEdge(matrix, Ax++, Ay++, Bx--, By--);
}
}
1 2 3 7 4 1
4 5 6 8 5 2
7 8 9 旋转为 9 6 3
public static void rotate(int[][] matrix){
int a=0,b=0,c= matrix.length-1,d=matrix[0].length-1;
while(a<c){
rotateEdge(matrix, a++, b++, c--, d--);
}
}
public static void rotateEdge(int[][] matrix, int a, int b, int c, int d) {
int tmp=0;
for(int i=0;i<d-b;++i){
tmp = matrix[a][b+i];
matrix[a][b+i] = matrix[c-i][b];
matrix[c-i][b] = matrix[c][d-i];
matrix[c][d-i] = matrix[a+i][d];
matrix[a+i][d] = tmp;
}
}
虽然需要扩容,但整体对时间复杂度影响较小,每次扩容都是常数时间的复杂度
哈希表的增删改查都是常数时间;
java中,基础类型按值传递,Integer类在-128-127之间是按值,其他范围是引用传递
但在哈希表中,基础类型和包装类都是按值传递,自定义类是按引用传递;
Map<String, String> map = new HashMap<>();
String a="123", b="123";
map.put(a, "333");
System.out.println(map.containsKey(b));//true
有序表的操作时间复杂度都是O(logN)
经常求数组arr[L,R]之间的累加和,设计数据结构方便存储和访问:
public class Node{
public int value;
public Node next;
public Node(int data){
value = data;
}
}
//或泛型
public static class Node<T>{
public T value;
public Node<T> next;
public Node(T data){
value = data;
}
}
public class DoubleNode{
public int value;
public DoubleNode last;
public DoubleNode next;
public DoubleNode(int data){
value = data;
}
}
//或泛型
public static class DoubleNode<T>{
public T value;
public DoubleNode<T> next;
public DoubleNode<T> last;
public DoubleNode(T data){
value = data;
}
}
public class reverseList {
public static class Node{
public int value;
public Node next;
public Node(int data){
value = data;
}
}
public static class DoubleNode{
public int value;
public DoubleNode next;
public DoubleNode last;
public DoubleNode(int data){
value = data;
}
}
public Node reverseLinkedList(Node head){//类似于头插法
Node pre = null;
Node next = null;
while(head!=null){
next = head.next;
head.next = pre;//修改指针指向
pre = head;
head = next;
}
return pre;
}
public DoubleNode reverseLinkedList(DoubleNode head){
DoubleNode pre = null;
DoubleNode next = null;
while(head!=null){
next = head.next;
head.next = pre;//修改指针指向
head.last = next;//反转
pre = head;
head = next;
}
return pre;
}
}
public Node removeValue(Node head, int value){//可能删除头节点,所以应该有返回的节点值
while(head!=null){
if(head.value!=value)
break;
head = head.next;
}
Node pre = head;
Node cur = head;//head是需要返回的头节点,因此利用cur来遍历
while(cur != null){
if(cur.value == value){
pre.next = cur.next;
}else{
pre = cur;
}
cur = cur.next;
}
return head;
}
public DoubleNode removeValue(DoubleNode head, int value){
while(head!=null){
if(head.value!=value){
break;
}
head = head.next;
}
DoubleNode pre = head;
DoubleNode cur = head;
while(cur!=null){
if(cur.value==value){
pre.next = cur.next;
cur.next.last = pre;
}else {
pre = cur;
}
cur = cur.next;
}
return head;
}
例如{1,4,2,6,3,7,1,5},以3个为一组,调整为{2,4,1,7,3,6,1,5}
public Node reverseK(Node head, int k){
Node start = head;
Node end = getKEnd(start, k);
if(end==null)
return head;
head = end;//最终返回的head保持不变,是第一组的最后一个节点
reverse(start, end);
Node lastEnd = start;
while (lastEnd!=null){
start=lastEnd.next;
end = getKEnd(start,k);
if(end==null)
return head;
reverse(start, end);
lastEnd.next = end;
lastEnd = start;
}
return head;
}
public static Node getKEnd(Node start, int k){
while (--k!=0&&start!=null)
start = start.next;//不够k个则返回null
return start;
}
public static void reverse(Node start, Node end){
end=end.next;
Node pre=null,cur=start,next=null;
while (cur!=end){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
start.next = end;
}
例,{4,3,6}+{2,5,3}={6,8,9},表示634+352=986
public Node add(Node head1, Node head2){
int len1=listLength(head1), len2=listLength(head2);
Node l = len1>=len2?head1:head2;
Node s = l==head1?head2:head1;
Node curl=l, curs=s,last=curl;
int curnum=0,carry=0;
while(curs!=null){//长短都有
curnum = curl.val + curs.val + carry;
carry = curnum/10;
curl.val = curnum%10;
last=curl;
curl=curl.next;
curs=curs.next;
}
while (curl!=null){//
curnum = curl.val + carry;
carry = curnum/10;
curl.val = curnum%10;
last = curl;
curl = curl.next;
}
if(carry!=0)
last.next = new Node(1);
return l;
}
public static int listLength(Node head){
int len=0;
while(head!=null){
len++;
head=head.next;
}
return len;
}
public static Node merge(Node head1, Node head2){
if(head1==null||head2==null)
return head1==null?head2:head1;
Node head = head1.val <= head2.val ? head1:head2;
Node cur1 = head.next;
Node cur2 = head==head1?head2:head1;
Node pre = head;
while (cur1!=null&&cur2!=null){
if(cur1.val <= cur2.val){
pre.next = cur1;
cur1 = cur1.next;
}else {
pre.next = cur2;
cur2 = cur2.next;
}
pre = pre.next;
}
pre.next = cur1==null?cur2:cur1;
return head;
}
双向链表实现双向队列,再通过单方向操作实现栈
public class DoubleListStack<T> {
public DoubleList_DoubleQueue<T> myStak;
public DoubleListStack(){
myStak = new DoubleList_DoubleQueue<T>();
}
public void push(T value){
myStak.enQueueFront(value);
}
public T pop(){
return myStak.deQueueFront();
}
}
注意判断栈满和栈空
维护一个最小栈:
入栈元素<=最小栈栈顶元素时将其压入最小栈;
出栈元素==最小栈栈顶元素时将其弹出最小栈
public static class Queue{
private Node head;
private Node tail;
private int size;
public Queue(){
head=null;
tail=null;
size = 0;
}
public boolean isEmpty(){
return size==0;
}
public int size(){
return size;
}
public void offer(int value){
Node n = new Node(value);
if(tail==null){
head=n;
tail=n;
}else{
tail.next=n;
tail=n;
}
size++;
}
public int poll(){
int ans = -1;
if(head!=null) {
ans = head.value;
head = head.next;
size--;
}
if(head==null)
tail=null;//否则tail指向的数据不会被释放,tail和head不一致
return ans;
}
}
public static class Stack{
private Node top;
int size;
public Stack(){
top=null;
size=0;
}
public boolean isEmpty(){
return size==0;
}
public int size(){
return size;
}
public void push(int value){
Node n =new Node(value);
if(top==null){
top = n;
}else{
n.next=top;
top=n;
}
size++;
}
public int pop(){
int ans=-1;
if(top!=null){
ans = top.value;
top=top.next;
size--;
}
return ans;
}
}
所有操作时间复杂度均为O(1)
package com.lqr.Stack;
import com.lqr.ListCode.removeValue;
public class DoubleList_DoubleQueue<T> {
public removeValue.DoubleNode<T> head;
public removeValue.DoubleNode<T> tail;
public void enQueueFront(T value){
removeValue.DoubleNode<T> cur = new removeValue.DoubleNode<T>(value);
if(head==null){
head = cur;
tail = cur;
}else {
cur.next = head;
head.last = cur;
head = cur;
}
}
public void enQueueTail(T value){
removeValue.DoubleNode<T> cur = new removeValue.DoubleNode<T>(value);
if(head == null){
head = cur;
tail = cur;
}else{
tail.next = cur;
cur.last = tail;
tail = cur;
}
}
public T deQueueFront(){
if(head == null){
return null;
}
removeValue.DoubleNode<T> cur = head;
if(head == tail){
head = null;
tail = null;
}else {
head = head.next;
head.last = null;
cur.next = null;
}
return cur.value;
}
public T deQueueTail(){
if(head == null){
return null;
}
removeValue.DoubleNode<T> cur = tail;
if(head == tail){
head = null;
tail = null;
}else {
tail = tail.last;
tail.next = null;
cur.last = null;
}
return cur.value;
}
}
双向链表先构造双向队列,再调用类和方法实现队列
public class DoubleListQueue<T> {
public DoubleList_DoubleQueue<T> myQueue;
public DoubleListQueue(){
myQueue = new DoubleList_DoubleQueue<T>();
}
public void enQueue(T value){
myQueue.enQueueTail(value);
}
public T deQueue(){
return myQueue.deQueueFront();
}
}
增加size和nextIndex()函数,不用再考虑front和tail之间的关系和越界问题
package com.lqr.Stack;
public class ArrayQueue {
private int[] queue;
private int front;
private int tail;
private int size;
private int max_size;
public ArrayQueue(int l){
queue = new int[l];
max_size = l;
front = 0;
tail = 0;
size = 0;
}
public boolean enqueue(int value){
if(size==max_size)
throw new RuntimeException("队列已满");
size++;
queue[tail]=value;
tail = netIndex(tail);
return true;
}
public int dequeue(){
if(size == 0){
throw new RuntimeException("队列为空");
}
size--;
int a = queue[front];
front = netIndex(front);
return a;
}
public int netIndex(int i){
return i>max_size?0:i+1;
}
}
两个栈分别为push和pop:
两个队列分别为data和help: