Java编程题集

Java编程题集

一、链表

1、从尾到头打印链表

题目描述:输入一个链表,从尾到头打印链表每个节点的值。
public class Solution {
    ArrayList<Integer> arrayList=new ArrayList<Integer>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode!=null){
            printListFromTailToHead(listNode.next);
            arrayList.add(listNode.val);
        }
        return arrayList;
    }
}

2、输出链表倒数第k个结点

题目描述:输入一个链表,输出该链表中倒数第k个结点。
public class Solution {  
    
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null)
            return null;
        ListNode p1=head;
		ListNode p2=head;
        for(int i=0;i<k;i++){
            if(p1==null)
                return null;
            p1=p1.next;
        }
        while(p1!=null){
            p1=p1.next;
            p2=p2.next;
        }
        return p2;
    }
}

3、反转链表

题目描述:输入一个链表,反转链表后,输出链表的所有元素
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head==null)
            return null;
        ListNode preNode=null;
        ListNode currentNode=null;
        while(head!=null){
            currentNode=head.next;
            head.next=preNode;
            preNode=head;
            head=currentNode;
        }
		return preNode;
    }
}

4、合并两个排序链表

题目描述:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null && list2!=null)
            return list2;
        if(list2==null && list1!=null)
			return list1;
        if(list2==null && list1==null)
			return null;
        ListNode head=null;
        if(list1.val<list2.val){
			head=list1;
            head.next=Merge(list1.next,list2);
        }else{
			head=list2;
            head.next=Merge(list1,list2.next);
        }
        return head;
    }
}

5、复杂链表的复制

题目描述:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
public class Solution {
    public RandomListNode Clone(RandomListNode head){
        if(head==null)
            return null;
        //开辟一个新节点
        RandomListNode p=new RandomListNode(head.label);
        p.random = head.random;
        //递归其他节点
        p.next=Clone(head.next);
        return p;
    }
}

6、链表中环的入口结点

题目描述:一个链表中包含环,请找出该链表的环的入口结点。
public class Solution {

    ListNode EntryNodeOfLoop(ListNode head){
        if(head == null || head.next == null)
            return null;
        ListNode p1 = head;
        ListNode p2 = head;
        while(p2 != null && p2.next != null ){
            p1 = p1.next;
            p2 = p2.next.next;
            if(p1 == p2){//可检测链表是否有环
                p2 = head;
                while(p1 != p2){
                    p1 = p1.next;
                    p2 = p2.next;
                }
                return p1;
            }
        }
        return null;
    }
}

7、删除有序链表中重复的结点(一个都不留)

题目描述:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
public class Solution {
    public ListNode deleteDuplication(ListNode head){
		if(head==null)
            return null;
        ListNode curNode=head;
        ListNode preNode=null;
        while(curNode!=null && curNode.next!=null){
            if(curNode.val!=curNode.next.val){
                preNode=curNode;
                curNode=curNode.next;
            }else{
                while(curNode.next!=null && curNode.val==curNode.next.val)
                    curNode=curNode.next;
                curNode=curNode.next;
                if(preNode!=null)
                	preNode.next=curNode;
                else
                    head=curNode;
            }
        }
        return head;
    }
}

8、删除无序链表中重复的结点(只留一个)

题目描述:在一个无序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点只留一个,返回链表头指针。例如,链表1->2->3->3->4->4->5 处理后为 1->2->3->4->5
public class Solution {
    
	public ListNode deleteDuplication(ListNode head){
		if(head==null)
			return null;
		ListNode p=head;
		while(p!=null){
			ListNode q=p;
			while(q.next!=null){
				if(p.val==q.next.val)
					q.next=q.next.next;
				else
					q=q.next;
			}
			p=p.next;
		}
		return head;
	}
}

9、查询单链表中间结点

题目描述:输出链表的中间结点
public class Solution {  
    
    public ListNode FindKthToTail(ListNode head){
		if(head==null)
			return null;
        ListNode p=head;
        ListNode q=head;
        while(p!=null && p.next!=null && p.next.next!=null){
            p=p.next.next;
            q=q.next;
        }
        return q;
    }
}

10、两链表的第一个公共结点

题目描述:输入两个链表,找出它们的第一个公共结点
public class Solution {
    
    public ListNode FindFirstCommonNode(ListNode head1, ListNode head2) {
        if(head1==null || head2==null)
            return null;
		ListNode p1=head1;
        int cou1=1;
        while(p1!=null){
            p1=p1.next;
            cou1++;
        }
 		ListNode p2=head2;
        int cou2=1;
        while(p2!=null){
            p2=p2.next;
            cou2++;
        }
        if(p1!=p2)	//不相等说明两链表不相交(可判断链表是否相交)
            return null;
        p1=head1;
        p2=head2;
        if(cou1>=cou2){
            int d=cou1-cou2;
            while(d>0){
                p1=p1.next;
                d--;
            }
        }else{
			int d=cou2-cou1;
            while(d>0){
                p2=p2.next;
                d--;
            }           
        }
        while(p1!=p2){
            p1=p1.next;
            p2=p2.next;
		}
        return p1;
    }
}

11、如何在不知头指针的情况下删除指定结点

分两种情况:
  • 若删除为链尾结点,则无法删除
  • 若删除不是链尾结点,则可以与后继结点交换值,然后删除后继结点
public class Solution {  
    
    public boolean deleteNode(ListNode node){
        if(node==null || node.next==null)
            return false;
        node.val=node.next.val;
        node.next=node.next.next;
        return true;
    }
}

二、栈和队列

1、栈和队列的实现

2、用两个栈实现队列

题目描述:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
    }
    
    public int pop() {
        if(stack2.isEmpty()){
        	while(!stack1.isEmpty())
        		stack2.push(stack1.pop());
        }
		return stack2.pop();
    }
}

3、栈的压人、弹出序列

题目描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack<Integer> s=new Stack<Integer>();
        int i=0,j=0;
        s.push(pushA[0]);
        while(j<popA.length){
            if(s.peek()==popA[j]){
               	s.pop();
               	j++;
            }else{
                i++;
                if(i<pushA.length)
            		s.push(pushA[i]);
                else
                    break;
            }
        }
      return s.isEmpty();
    }
}

三、排序

四、位运算

1、如何用移位操作实现乘法运算

public int power(int m,int n){	//m乘以2^n次方
	for(int i=0;i<n;i++)
		m=m<<1;
	return m;
}

2、如何判断一个数是否为2的n次方

public boolean isPower(int n){
	if(n<1)
		return false;
	int m=n&(n-1);
	return m==0;
}

3、如何求二进制数中1的个数

以下算法的时间复杂度为O(n),其中n代表二进制数的位数。
public int countOne(int n){
	int count=0;
	while(n>0){
		if((n&1)==1)//判断最后一位是否为1
			count++;
		n>>=1;
	}
	return count;
}
以下算法的时间复杂度为O(m),其中m代表二进制数中1的个数。
public int countOne(int n){
	int count=0;
	while(n>0){
		if(n!=0){
			n=n&(n-1);
			count++;
		}
	}
	return count;
}

4、整数中1出现的次数(从1到n整数中出现的次数)

题目描述:求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
    	StringBuilder s=new StringBuilder();
    	for(int i=1;i<=n;i++)
    		s.append(i);
    	char[] c=s.toString().toCharArray();
    	int count=0;
    	for(int i=0;i<c.length;i++)
    		if(c[i]=='1')
    			count++;
		return count;
    }
}

五、数组

1、如何寻找数组中的最小值与最大值

public int[] GetMaxAndMin(int[] arr){
	int max=arr[0],min=arr[0];
	for(int i=1;i<arr.length;i++){
		if(max<arr[i])
			max=arr[i];
		else if(min>arr[i])
			min=arr[i];
	}
	int[] a=new int[2];
	a[0]=min;a[1]=max;
	return a;
}

2、找出数组中第二大数

public int[] GetMaxAndMin(int[] arr){
	int max=arr[0],min=arr[0];
	for(int i=1;i<arr.length;i++){
		if(max<arr[i])
			max=arr[i];
		else if(min>arr[i])
			min=arr[i];
	}
	int[] a=new int[2];
	a[0]=min;a[1]=max;
	return a;
}

3、连续子数组的最大和

例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。
public int MaxSubArray(int[] arr){
	int nALL=arr[0];
	int nEnd=arr[0];
	for(int i=1;i<arr.length;i++){
		nEnd=max(nEnd+arr[i],arr[i]);
		nALL=max(nEnd,nALL);
	}
	return nALL;
}
public int max(int m,int n){
	return m>n?m:n;
}

4、如何把一个数组循环右移k位

如:把数组12345678右移2位为78123456。
public void shift_k(int[] arr,int k){
	int n=arr.length;
	k=k%n;
	reverse(arr,n-k,n-1);
	reverse(arr,0,n-k-1);
	reverse(arr,0,n-1);
}
public void reverse(int[] arr,int b,int e){
	for(;b<e;b++,e--){
		int temp=arr[b];
		arr[b]=arr[e];
		arr[e]=temp;
	}
}

5、找出数组中只出现一次的数字

题目描述:一个整型数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度为O(n),空间复杂度为O(1)
public int findNotDouble(int[] arr){
	int s=arr[0];
	for(int i=01;i<arr.length;i++)
		s^=arr[i];		//利用异或
	return s;
}
延伸:一个整型数组里除了一个数字之外,其他的数字都出现了n次。
public int findNotDouble(int[] arr,int n){
	int[] bitCount=new int[32];
	for(int i=0;i<arr.length;i++)
		for(int j=0;j<32;j++)
			bitCount[j]+=((arr[i]>>j)&1);
	int result=0;
	for(int i=0;i<32;i++)
		if(bitCount[i]%n!=0)
			result+=(1<<i);
	return result;
}

6、合并两个有序数组

题目描述:对两个有序的子数组进行合并,要求空间复杂度为O(1)。如给定数组a={1,2,6,7,9,2,4,8,10,13,14},mid=5,则a[0]~a[4]是有序的,a[5]~a[10]是有序的,合并后的数组为{1,2,4,5,6,7,8,9,10,13,14}
public void sort(int[] a,int mid){
	int tmp;
	for(int i=0;i<=mid-1;i++){
		if(a[mid]<a[i]){
			tmp=a[i];
			a[i]=a[mid];
			a[mid]=tmp;
			Right(a,mid);
		}
	}
}
public void Right(int[] a,int mid){
	int len=a.length;
	int tmp;
	for(int i=mid;i<len-1;i++){
		if(a[i+1]<a[i]){
			tmp=a[i];
			a[i]=a[i+1];
			a[i+1]=tmp;
		}
	}
}

7、找出两个有序数组的交集

题目描述:假设两个有序的数组a和b,其中a={0,1,2,3,4},b={1,3,5,7,9},那么它们的交集为{1,3}
public void mixed(int[] a,int[] b){
	int i=0,j=0;
	int n1=a.length,n2=b.length;
	while(i<n1 && j<n2){
		if(a[i]==b[j]){
			System.out.print(a[i]+" ");//打印交集值
			i++;j++;
		}else if(a[i]<b[j])
			i++;
		else
			j++;		
	}
}

8、判断一个数组中数值是否连续相邻

判断5个数值是否连续相邻,5个数可以连续,如{8,7,5,0,6},其中0可以看做任何数,则全0算连续,只有一个非0算连续。
public boolean isContinuous(int [] a) {
    if(a==null || a.length==0)
        return false;
    int min=-1,max=-1,count0=0;
    for(int i=0;i<a.length;i++){
        if(a[i]!=0){
            if(min>a[i] || min==-1)
                min=a[i];
            if(max<a[i] || max==-1)
                max=a[i];
        }else
            count0++;
    }
    if(count0>=a.length-1)
        return true;
    if(max-min==a.length-1)
        return true;
    else
        return false;
}

9、二维数组中的查找

题目描述:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
public boolean Find(int target, int [][] array) {
	int i=array.length-1;
	int j=0;
	while(i>=0 && j<array[0].length){
		if(array[i][j] > target)
			i--;
		else if(array[i][j] < target)
			j++;
		else
			return true;
	}
	return false;
}

10、调整数组顺序使奇数位于偶数前面

题目描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分。
public void reOrderArray(int [] arr) {
	int i=0,j=arr.length-1;
	while(i<j){
		while(i<j && arr[i]%2!=0)
			i++;
		while(i<j && arr[j]%2==0)
			j--;
		int tmp=arr[i];
		arr[i]=arr[j];
		arr[j]=tmp;
	}
}
如果要保证奇数和奇数,偶数和偶数之间的相对位置不变。
public void reOrderArray(int [] array) {
    int scr[]=new int[array.length];
    int l=0,r=array.length-1;
    for(int i=0;i<array.length;i++){
        if(array[i]%2==0){
            scr[r]=array[i];
            r--;
        }else{
            scr[l]=array[i];
            l++;
        }
    }
    for(int i=0;i<l;i++)
		array[i]=scr[i];
    for(int i=array.length-1;i>r;i--)
		array[l++]=scr[i];
}

11、丑数

题目描述:把只包含素因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
public int GetUglyNumber_Solution(int index){
    if (index < 7)
        return index;
    int[] res=new int[index];
    res[0] = 1;
    int t2=0,t3=0,t5=0,i;
    for (i=1;i<index;++i){
        res[i]=min(res[t2]*2,min(res[t3]*3,res[t5]*5));
        if (res[i]==res[t2]*2)
        	t2++;
        if (res[i]==res[t3]*3)
        	t3++;
        if (res[i]==res[t5]*5)
        	t5++;
    }
    return res[index-1];
}

private int min(int a, int b) {
    return (a>b)?b:a;
}

12、数字在排序数组中出现的次数

题目描述:统计一个数字在排序数组中出现的次数。
public int binarySearch(int first,int last,int[] array,int target){
    int mid=(first+last)/2;
    int count=0;
    if(first>last)
        return 0;
    else if(target==array[mid]){
        count=1;
        int l=mid-1,r=mid+1;
        while(l>=first){
            if(target==array[l]){
                count++;
                l--;
            }else
                break;
        }
        while(r<=last){
            if(target==array[r]){
                count++;
                r++;
            }else
                break;
        }
        return count;
    }else if(target<array[mid])
        count=binarySearch(first,mid-1,array,target);
    else
        count=binarySearch(mid+1,last,array,target);
    return count;
}

13、和为S的连续正数序列

题目描述:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
    ArrayList<ArrayList<Integer>> listAll=new ArrayList();
    if(sum<=1)
        return listAll;
    for(int i=1;i<=sum/2+1;i++){
        int s=0;
        ArrayList<Integer> list=new ArrayList();
        for(int j=i;j<=sum/2+1;j++){
            list.add(j);
            s+=j;
            if(s>sum)
                break;
            else if(s==sum){
               listAll.add(list);
                break;
            }                 
        }
    }
    return listAll;
}

14、和为S的两个数字

题目描述:输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。 
public void FindNumbersWithSum(int [] arr,int sum) {
	if (arr == null || arr.length < 2) 
		return;
	int i=0,j=arr.length-1;
	while(i<j){
		if(arr[i]+arr[j]==sum){
			System.out.println(arr[i]+" "+arr[j]);
			return;
		}else if(arr[i]+arr[j]>sum){
			j--;
		}else{
			i++;
		}
	}
}

15、构建乘积数组

题目描述:给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法
思路:基于动态规划,先求i-1个和后n-i个乘积,保存之后求B[i]
public int[] multiply(int[] A) {
	int n=A.length;
	int[] B=new int[n];
	int[] C=new int[n];
	B[0]=1;C[n-1]=1;
	for(int i=1;i<n;i++){
		B[i]=B[i-1]*A[i-1];
		C[n-1-i]=C[n-i]*A[n-i];
	}
	for(int i=0;i<n;i++)
		B[i]=C[i]*B[i];
	return B;
}

16、顺时针打印矩阵

题目描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
public class Solution {
    public static ArrayList<Integer> printMatrix(int [][] matrix) {
        if(matrix.length==0)
            return null;
       	ArrayList<Integer> list=new ArrayList();
        int rowStart=0,colStart=0;
        int rowEnd=matrix.length-1;
        int colEnd=matrix[0].length-1;
        while(rowStart<=rowEnd && colStart<=colEnd){
            for(int i=colStart;i<=colEnd;++i)
                list.add(matrix[rowStart][i]);
            for(int i=rowStart+1;i<=rowEnd;++i)
                list.add(matrix[i][colEnd]);
            if(rowStart!=rowEnd)
	            for(int i=colEnd-1;i>=colStart;--i)
	                list.add(matrix[rowEnd][i]);
	        if(colStart!=colEnd)
	            for(int i=rowEnd-1;i>rowStart;--i)
	                list.add(matrix[i][colStart]);
            rowStart++;
            colStart++;
            rowEnd--;
            colEnd--;
        }
        return list;
    }
}

六、字符串

1、字符串反转

题目描述:把一个句子中的单词进行反转,例如,“how are you”,进行反转后位“you are how”
public String ReverseSentence(String str) {
	StringBuilder s=new StringBuilder();
	int index=str.length();
	for(int i=str.length()-1;i>=0;i--){
		char c=str.charAt(i);
		if(c==' '){
			s.append(str.substring(i+1,index)+" ");
			index=i;
		}
	}
	s.append(str.substring(0,index));
	return s.toString();
}

2、如何删除字符串中重复的字符

题目描述:删除字符串中重复的字符,例如,“good”变成“god”
public String RemoveDuplicate(String str) {
	StringBuilder s=new StringBuilder();
	char[] ch=str.toCharArray();
	for(int i=0;i<ch.length;i++){
		if(ch[i]!='\0'){
			s.append(ch[i]);
			for(int j=i+1;j<ch.length;j++){
				if(ch[i]==ch[j])
					ch[j]='\0';
			}
		}
	}
	return s.toString();
}

3、如何统计一行字符中有多少个单词

单词的数目可以由空格出现的次数决定(连续的若干个空格作为出现一次空格;一行开头的空格不统计在内)。
public int wordCount(String str) {
	int l=str.length();
	int count=0,word=0;
	for(int i=0;i<l;i++){
		if(str.charAt(i)==' ')
			word=0;
		else if(word==0){
			word=1;
			count++;
		}
	}
	return count;
}

4、输出字符串的所有组合

题目描述:输入一个字符串,输出该字符串中字符的全部组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc
public ArrayList<String> Combine(String str){
	char[] ch=str.toCharArray();
	int len=ch.length;
	boolean used[]=new boolean[len];
	char cache[]=new char[len];
	int result=len;
	ArrayList<String> list=new ArrayList<>();
	while(true){
		int index=0;
		while(used[index]){
			used[index]=false;
			result++;
			if(++index==len)
				return list;
		}
		used[index]=true;
		cache[--result]=ch[index];
		list.add(new String(cache).substring(result));
	}
}

5、输出字符串的排列

题目描述:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
//调用时printAllArray(list,"abc","")
public void printAllArray(ArrayList<String> list,String s,String n) {
	if (s.length()==0) {
		if(!list.contains(n))
			list.add(n); 
	} else {  
		for (int i=0; i<s.length();++i) {  
			printAllArray(list,s.substring(1),n+s.charAt(0));  
			s=s.substring(1)+s.charAt(0);  
		}  
	}  
} 

6、第一个只出现一次的字符

题目描述:在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置。如果字符串为空,返回-1
public int FirstNotRepeatingChar(String str) {
    if(str==null)
        return -1;
    char[] ch=str.toCharArray();
    HashMap<Character, Integer> map=new HashMap<>();
    for(int i=0;i<ch.length;i++){
    	if(!map.containsKey(ch[i]))
    		map.put(ch[i],1);
    	else
    		map.put(ch[i],map.get(ch[i])+1);
    }
    for(int i=0;i<ch.length;i++){
    	if(map.get(ch[i])==1)
    		return i;
    }
    return -1;
}

七、二叉树

1、实现二叉查找树

public class BinarySearchTree {
	
	private Node root;
	public BinarySearchTree() {
		root=null;
	}
	
	public void add(int val){
		Node newNode=new Node(val);
		if(root==null)
			root=newNode;
		else{
			Node current=root;
			Node parent;
			while(true){
				parent=current;
				if(val<current.val){
					current=current.left;
					if(current==null){
						parent.left=newNode;
						return;
					}
				}else{
					current=current.right;
					if(current==null){
						parent.right=newNode;
						return;
					}
				}
			}
		}
	}
	
	public void buildTree(int[] arr){
		for(int i=0;i<arr.length;i++)
			add(arr[i]);
	}
	
	public void preOrder(){
		System.out.print("先序遍历:");
		preOrder(root);
	}
	
	private void preOrder(Node node){
		if(node!=null){
			System.out.print(node.val+" ");
			preOrder(node.left);
			preOrder(node.right);
		}
	}
	
	private class Node{
		public int val;
		public Node left;
		public Node right;
		public Node(int val){
			this.val=val;
			this.left=null;
			this.right=null;
		}
	}
}

2、根据先序和中序重建二叉树

题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
public class Solution {
	public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
		TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
		return root;
	}

	private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
		if(startPre>endPre||startIn>endIn)
			return null;
		TreeNode root=new TreeNode(pre[startPre]);

		for(int i=startIn;i<=endIn;i++)
			if(in[i]==pre[startPre]){
				root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
				root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn);
			}
		return root;
	}
}

3、树的子结构

题目描述:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1==null || root2==null)
            return false;
        boolean flag = false;
        if(root1.val==root2.val){
            flag = isSubtree(root1,root2);
        }
        if(!flag){
            flag = HasSubtree(root1.left, root2);
            if(!flag){
                flag = HasSubtree(root1.right, root2);
            }
        }
        return flag;
    }
    
    public boolean isSubtree(TreeNode root1,TreeNode root2){
        if(root2==null) 
            return true;
        if(root1==null && root2!=null) 
            return false;  
        if(root1.val==root2.val)
            return isSubtree(root1.left,root2.left) && isSubtree(root1.right,root2.right);
        else 
            return false;
    }
}

4、二叉树镜像

题目描述:操作给定的二叉树,将其变换为源二叉树的镜像。 
输入描述:二叉树的镜像定义:源二叉树 
       8
      /   \
     6   10
    / \  / \
    5  7 9 11
镜像二叉树
       8
      /   \
    10   6
    /  \    /  \
  11  9 7  5
public class Solution {
    public void Mirror(TreeNode root) {
        if(root==null || (root.left==null&&root.right==null))
            return;
        TreeNode temp=root.left;
        root.left=root.right;
        root.right=temp;
        if(root.left!=null)
        	Mirror(root.left);
        if(root.right!=null)
        	Mirror(root.right);
    }
}

5、层序遍历二叉树

题目描述:从上往下打印出二叉树的每个节点,同层节点从左至右打印。
public class Solution {
    public ArrayList<Integer> LevelTraverse(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(root == null) 
        	return list;
	Deque<TreeNode> deque=new LinkedList<>();
        deque.add(root);
        TreeNode currentNode=null;
        while(!deque.isEmpty()){
            currentNode=deque.remove();
            list.add(currentNode.val);
            if(currentNode.left!=null)
                deque.add(currentNode.left);
            if(currentNode.right!=null)
                deque.add(currentNode.right);
        }
        return list;
    }
}

6、后序遍历二叉树

public class Solution {

    public ArrayList<Integer> PostorTraverse(TreeNode root) {
    	ArrayList<Integer> list = new ArrayList<Integer>();
        if(root==null)
            return list;
	return Traverse(root,list);
    }
    public ArrayList<Integer> Traverse(TreeNode root,ArrayList<Integer> list) {
		if(root!=null){
			list=Traverse(root.left,list);
			list=Traverse(root.right,list);
			list.add(root.val);
		}
		return list;
    }
}

7、中序遍历二叉树

public class Solution {

    public ArrayList<Integer> inderTraverse(TreeNode root) {
    	ArrayList<Integer> list = new ArrayList<Integer>();
        if(root==null)
            return list;
	return Traverse(root,list);
    }
    public ArrayList<Integer> Traverse(TreeNode root,ArrayList<Integer> list) {
		if(root!=null){
			list=Traverse(root.left,list);
            list.add(root.val);
			list=Traverse(root.right,list);
		}
		return list;
    }
}

8、先序遍历二叉树

public class Solution {

    public ArrayList<Integer> inderTraverse(TreeNode root) {
    	ArrayList<Integer> list = new ArrayList<Integer>();
        if(root==null)
            return list;
	return Traverse(root,list);
    }
    public ArrayList<Integer> Traverse(TreeNode root,ArrayList<Integer> list) {
		if(root!=null){
            list.add(root.val);
			list=Traverse(root.left,list);
			list=Traverse(root.right,list);
		}
		return list;
    }
}

9、判断是否为二叉查找树的后序遍历序列

题目描述:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。完美的递归定义
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length == 0) 
        	return false;
        return IsTreeBST(sequence, 0, sequence.length-1);
    }
    public boolean IsTreeBST(int [] sequence,int start,int end ){
        if(end <= start)
            return true;
        int i = start;
        for (; i < end; i++)
            if(sequence[i] > sequence[end])
                break;
        for (int j = i; j < end; j++)
            if(sequence[j] < sequence[end])
                return false;
        return IsTreeBST(sequence, start, i-1) && IsTreeBST(sequence, i, end-1);
    }
}

10、二叉树中和为某一值的路径

题目描述:输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
public class Solution {
    private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
    private ArrayList<Integer> list = new ArrayList<Integer>();
    
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(root == null) 
        	return listAll;
        list.add(root.val);
        target -= root.val;
        if(target == 0 && root.left == null && root.right == null)
            listAll.add(new ArrayList<Integer>(list));
        FindPath(root.left, target);
        FindPath(root.right, target);
        list.remove(list.size()-1);
        return listAll;
    }
}

11、二叉搜索树与双向链表

题目描述:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
public class Solution {

	public TreeNode Convert(TreeNode pRootOfTree) {
		TreeNode lastNode =null;
		TreeNode headNode=ConvertNode(pRootOfTree, lastNode);
		while (headNode != null && headNode.left != null)
			headNode = headNode.left; 
		return headNode;  
	}

	public TreeNode ConvertNode(TreeNode rootTree, TreeNode lastNode) {  
		if (rootTree == null)
			return null;  
		if (rootTree.left != null) 
			lastNode=ConvertNode(rootTree.left, lastNode);  
		rootTree.left = lastNode;  
		if (lastNode != null) 
			lastNode.right = rootTree;  
		lastNode = rootTree;  
		if (rootTree.right != null) 
			lastNode=ConvertNode(rootTree.right, lastNode);  
		return lastNode;  
	} 
}

12、二叉树的深度

题目描述:输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
public int TreeDepth(TreeNode root) {
    if(root==null)
        return 0;
    return 1+Math.max(TreeDepth(root.left),TreeDepth(root.right));
}

13、判断某二叉树是否为平衡二叉树

题目描述:输入一棵二叉树。判断该二叉树是否为平衡二叉树
public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root==null)
            return true;
        if(Math.abs(TreeDepth(root.left)-TreeDepth(root.right))>1)
            return false;
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
    }
    public int TreeDepth(TreeNode root) {
        if(root==null)
            return 0;
        return 1+Math.max(TreeDepth(root.left),TreeDepth(root.right));
    }
}

14、输出给定结点的中序遍历的下一个结点

题目描述:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode node){
        if(node==null)
            return null;
        if(node.right!=null){
            node=node.right;
            while(node.left!=null)
                node=node.left;
            return node;
        }
        while(node.parent!=null){
            if(node.parent.left==node)
                return node.parent;
            node=node.parent;
        }
        return null;
    }
}

15、判断二叉树是否对称

题目描述:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
public class Solution {
    boolean isSymmetrical(TreeNode pRoot){
        if(pRoot==null)
            return true;
        return comRoot(pRoot.left,pRoot.right);
    }
    private boolean comRoot(TreeNode left, TreeNode right) {
        if(left == null) 
        	return right==null;
        if(right == null)
            return false;
        if(left.val != right.val) 
            return false;
        return comRoot(left.right, right.left) && comRoot(left.left, right.right);
    }
}

16、二叉查找树的第k个结点

题目描述:给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
public class Solution {
    public int count=0;
    public TreeNode FindNode=null;
    TreeNode KthNode(TreeNode root, int k){
        if(root==null)
            return null;
        return Find(root,k);
    }
    public TreeNode Find(TreeNode root,int k) {
        if(root!=null && count<k){  
            FindNode=Find(root.left,k);  
            count++;
        	if(count==k)
            	return root; 
            FindNode=Find(root.right,k);  
        }  
        return FindNode;  
    } 
}

八、其他

1、斐波那契数列

题目描述:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
public class Solution {
    public int Fibonacci(int n) {
        if(n==0)
            return 0;
        else if(n <= 2){  
            return 1;  
        }else{  
            int i=2;
            int a=1,b=1,sum=0;
            while(i!=n){
                i++;
                sum=a+b;
                a=b;
                b=sum;
            }
            return sum;
        }  
    }
}

2、跳台阶

题目描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

public class Solution {
    public int JumpFloor(int target) {
		if(target<=2)
            return target;
        else{
            int i=2;
            int a=1,b=2,sum=0;
            while(i!=target){
                i++;
                sum=a+b;
                a=b;
                b=sum;
            }
            return sum;
        }
    }
}

3、变态跳台阶

题目描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法
public class Solution {
    public int JumpFloorII(int target) {
        if(target<=1)
            return target;
        int s=1;
        while(target>1){
            s=s*2;
            target--;
        }
        return s;
    }
}

4、矩形覆盖

题目描述:我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
public class Solution {
    public int RectCover(int target) {
		if(target<=2)
            return target;
        int a=1,b=2,sum=0;
        while(target>2){
            target--;
            sum=a+b;
            a=b;
            b=sum;
        }
        return sum;
    }
}

九、算法题

牛客网左程云:https://www.nowcoder.com/live/11?page=1

1、最长递增子序列

题目描述:求出给定序列的最长递增子序列的长度,给定序列不是有序的,子序列不是子数组,元素在原数组中不必是连续的
/*
solutions1: 时间复杂度O(n^2),空间复杂度O(n)
 新建一个辅助数组h,h[i]表示以nums[i]结尾的最长递增子序列的长度
h[i]值的确定,需要nums[i]与nums[j](0<=j<i)做比较,h[i] = h[j] + 1 (when nums[j] < nums[i]),找出这些值中的最大值即最终的nums[i]
如果nums[i]最小的话,则h[i] = 1;
因为h[i]值的确定需要nums[i]进行最多i-1次比较,所以时间复杂度为O(n^2)
*/
public int getLonggestLen(int[] arr) {
	int n=arr.length;
	int[] h=new int[n];
	int maxL=0;
	for(int i=0;i<n;i++){
		int j=i-1;
		int k=1;
		while(j>-1){
			if(arr[j]<arr[i])
				k=Math.max(k,h[j]+1);
			j--;
		}
		h[i]=k;
		maxL=Math.max(maxL, k);
	}
	return maxL;
}
/*
solution2:  时间复杂度O(n*log(n)),空间复杂度O(n)
新建一个辅助数组h,h[i]表示遍历到当前nums[j]位置时长度为i+1的递增子序列的最小末尾
h填入值的部分称作有效区,还未填入值的部分称作无效区
h[i]值的确定,h[0] = nums[0],在遍历nums 的同时,在h[i]中找到第一个大于等于nums[j]的数,并将其替换为为nums[j],如果没找到则将h有效区后一个元素置为nums[j]
h[i]会一直保持有序状态,所以找第一个大于等于nums[j]的数可以用二分法,最后返回h有效区的长度即可
由于在遍历的同时采用了时间复杂度为log(n)的二分查找,故时间复杂度为O(n*log(n))
 */
public int getLonggestLen(int[] nums) {
	int maxLen = 0;
	if (nums.length == 0)
		return maxLen;
	int[] h = new int[nums.length];
	h[0] = nums[0];
	maxLen = 1;
	for (int i = 1; i < nums.length; i++) {
		int pos = getPos(h, 0, maxLen - 1,nums[i]);
		if (pos == -1) 
			h[maxLen++] = nums[i];                
		else 
			h[pos] = nums[i];
	}
	return maxLen;
}
public int getPos(int[] h, int left, int right, int num) {
	if (left <= right) {
		int mid = left + (right - left) / 2;
		if (h[mid] >= num) {
			int pos = getPos(h, left, mid - 1, num);
			if (pos == -1)
				return mid;
			return pos;
		} else 
			return getPos(h, mid + 1, right, num);                          
	}
	return -1;
}

2、水的容量

给定一个非负数的数组,代表一个容器。例如数组[0,1,0,2,1,0,1,3,2,1,2,1],就是以下图形中黑色的部分。如果用这个容器接水的话,请问可以接多少水?还以这个数组为例,可以接6 格水,就是以下图形中蓝色的部分。

public int storeWater(int[] arr){
	int count=0;
	int l_max=arr[0];
	for(int i=1;i<arr.length-1;i++){
		if(l_max<arr[i-1])
			l_max=arr[i-1];
		int j=i+1;
		int r_max=arr[i+1];
		while(j<arr.length){
			r_max= Math.max(r_max, arr[j]);
			j++;
		}
		int a=Math.min(l_max, r_max)-arr[i];
		if(a>0)
			count+=a;
	}
	return count;
}

3、求两柱子围成面积最大

给定一个非负数的数组,数组中的每个值代表一个柱子的高度,柱子的宽度是1。两个柱子之间可以围成一个面积,规定:面积=两根柱子的最小值*两根柱子之间的距离。比如数组[3,4,2,5]。3 和4 之间围成的面积为0,因为两个柱子是相邻的,中间没有距离。3 和2 之间围成的面积为2,因为两个柱子的距离为1,且2 是最短的柱子,所以面积=1*2。求在一个数组中,哪两个柱子围成的面积最大,并返回值。
public int maxArea(int[] arr){
	int l=0;
	int r=arr.length-1;
	int max=Integer.MIN_VALUE;
	while(l<r){
		if(arr[l]<arr[r])
			max=Math.max(max, (r-l-1)*arr[l++]);
		else
			max=Math.max(max, (r-l-1)*arr[r--]);
	}
	return max;
}

4、子数组最大和

如数组:[3,-2,1,-6,3,2,-1,3,-2],则子数组[3,2,-1,3]为最大
public int maxSum(int[] arr){
	int res=arr[0];
	int cur=arr[0];
	for(int i=1;i<arr.length;i++){
		cur=cur<0 ? 0:cur;
		cur+=arr[i];
		res=Math.max(res, cur);
	}
	return res;
}

5、求两个子数组最大的累加和

给定一个数组,其中当然有很多的子数组,在所有两个子数组的组合中,找到相加和最大的一组,要求两个子数组无重合的部分。最后返回累加和。(时间复杂度达到O(N))
public int maxSum1(int[] arr){
	int n=arr.length;
	int[] h=new int[n];
	h[n-1]=arr[n-1];
	int cur=arr[n-1];
	for(int i=n-2;i>=0;i--){
		cur=cur<0 ? 0:cur;
		cur+=arr[i];
		h[i]=Math.max(h[i+1], cur);
	}
	int res=arr[0]+h[1];
	int lmax=arr[0];
	for(int i=1;i<n-1;i++){
		cur=cur<0 ? 0:cur;
		cur+=arr[i];
		lmax=Math.max(lmax, cur);
		res=Math.max(res, lmax+h[i+1]);
	}
	return res;
}

6、未排序正数数组中累加和为给定值的最长子数组长度

给定一个数组arr,该数组无序,但每个值均为正数,再给定一个正数k。求arr的所有子数组中所有元素相加和为k 的最长子数组长度。例如,arr=[1,2,1,1,1],k=3。累加和为3 的最长子数组为[1,1,1],所以结果返回3。
public int maxLength(int[] arr,int k){
	int n=arr.length;
	int l=0,r=0;
	int sum=arr[0];
	int len=0;
	while(r<n){
		if(sum==k){
			len=Math.max(len, r-l+1);
			sum-=arr[l++];
		}else if(sum<k){
			r++;
			if(r==n)
				break;
			sum+=arr[r];
		}else
			sum-=arr[l++];
	}
	return len;
}

7、给定一个无序矩阵,其中有正,有负,有0,求子矩阵的最大和。

public int maxLength(int[][] arr){
	int row=arr.length;
	int col=arr[0].length;
	int cur=0;
	int max=Integer.MIN_VALUE;
	for(int i=0;i<row;i++){
		int[] subArr=new int[col];
		for(int j=i;j<row;j++){
			cur=0;
			for(int k=0;k<col;k++){
				subArr[k]+=arr[j][k];
				cur+=subArr[k];
				max=Math.max(cur, max);
				cur=cur<0?0:cur;
			}
		}
	}
	return max;
}

8、累加和<=k的最大子数组大小

public int maxLength(int[] arr,int k){
	int n=arr.length;
	int[] h=new int[n+1];
	int sum=0;
	h[0]=sum;
	for(int i=0;i<n;i++){
		sum+=arr[i];
		h[i+1]=Math.max(sum, h[i]);
	}
	sum=0;
	int res=0;
	int pre=0;
	int len=0;
	for(int i=0;i<n;i++){
		sum+=arr[i];
		pre=getLessIndex(h, sum-k);
		len = pre==-1 ? 0 : i-pre+1;
		res=Math.max(res, len);
	}
	return res;
}

public int getLessIndex(int[] arr,int num){
	int low=0;
	int high=arr.length-1;
	int mid=0;
	int res=-1;
	while(low<=high){
		mid=(low+high)/2;
		if(arr[mid]>=num){
			res=mid;
			high=mid-1;
		}else
			low=mid+1;
	}
	return res;
}

9、累加和<=k的最大子矩阵大小

public int maxSubMatrixSumLess(int[][] m,int sum){
	int res=0;
	int row=m.length;
	int col=m[0].length;
	for(int i=0;i<row;i++){
		int[] sumArr=new int[col];
		for(int j=i;j<row;j++){
			for(int k=0;k<col;k++)
				sumArr[k]+=m[j][k];
			res=Math.max(res, (j-i+1)*maxLength(sumArr, sum));//子函数在第8题
		}
	}
	return res;
}












猜你喜欢

转载自blog.csdn.net/hjy132/article/details/67634316