剑指Offer系列之题36~题40
36.数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
。
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
归并排序,排序的过程中判断逆序对的数量
1.
public class Solution {
int count=0;
public int InversePairs(int [] array) {
//遍历不可取,时间复杂度 O(n^2)
if(array.length<=1)
return 0;
//使用归并排序
mergeSort(array,0,array.length-1);
return count;
}
public void mergeSort(int a[],int low,int high){
if(low<high){
int mid=(low+high)/2;
mergeSort(a,low,mid);//分别对两边排序
mergeSort(a,mid+1,high);
//合并,此时传进去的部分已经是有序态
merge(a,low,mid,high);
}
}
public void merge(int a[],int low,int mid,int high){
//辅助数组存储排序后的元素
int b[]=new int[high-low+1];
int i=0;int j=0;int k=0;
for(i=low,j=mid+1,k=0;i<=mid && j<=high;k++ ){
if(a[i]<=a[j])
b[k]=a[i++];
else{ //左边大于右边 此时逆序
count=count+mid-i+1;
if(count>=1000000007)
count%=1000000007;
b[k]=a[j++];
}
}
while(i<=mid)//将剩余元素复制到辅助数组
b[k++]=a[i++];
while(j<=high)
b[k++]=a[j++];
//将辅助数组的值放到原数组
for(k=0;k<b.length;k++){
a[k+low]=b[k];
}
}
}
37.两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
双指针,一个指针走到末尾节点后从另一条链表首节点接着走,当两者走到第一个交点,走的距离相等。
1.双指针:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1==null || pHead2==null)
return null;
//双指针
ListNode p1=pHead1;
ListNode p2=pHead2;
int count1=0;
int count2=0;
while(p1!=p2){//当两指针未相遇时
if(p1.next==null){//当p1走到末尾,切换到链表2
p1=pHead2;
count1++;
}else{
p1=p1.next;
}
if(p2.next==null){//当p2走到末尾,切换到链表1
p2=pHead1;
count2++;
}else{
p2=p2.next;
}
if(count1>1 || count2>1)//当某一指针已切换两次仍未相遇,证明没有交点
return null;
}
return p1;
}
}
2.精简版:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1==null || pHead2==null)
return null;
//双指针
ListNode p1=pHead1;
ListNode p2=pHead2;
while(p1!=p2){//当两指针未相遇时
//没有交点时会在null相遇
p1=(p1==null? pHead2:p1.next);
p2=(p2==null? pHead1:p2.next);
}
return p1;
}
}
38.数字在排序数组中出现的次数
统计一个数字在排序数组中出现的次数。
暴力解:直接遍历到相等的数字,然后从该数字开始遍历直到数字不等。
1.暴力解:
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array.length<=0)
return 0;
//暴力解:先找到所在位置,然后计数,直到不等于该数字
int count=0;
int i=0;
for(;i<array.length;++i){
if(array[i]==k){
break;
}
}
for(;i<array.length;++i){
if(array[i]!=k)
break;
count++;
}
return count;
}
}
2.二分查找:
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array.length<=0)
return 0;
//二分查找:找到第一个k和最后一个k的位置
int count=0;
int first=getFirstK(array,0,array.length-1,k);
int last=getLastK(array,0,array.length-1,k);
if(first!=-1 && last!=-1)
count=last-first+1;
return count;
}
public int getFirstK(int a[],int low,int high,int k){
if(low>high)
return -1;
int mid=0;
while(low<=high){
mid=(low+high)>>1;
if(a[mid]>k){//第一个k在其左边
high=mid-1;
}else if(a[mid]<k){//<k,此时第一个k在其右边
low=mid+1;
}else{//=k
if(mid>0 && a[mid-1]==k)
high=mid-1;
else
return mid;
}
}
return -1;
}
public int getLastK(int a[],int low,int high,int k){
if(low>high)
return -1;
int mid=0;
while(low<=high){
mid=(low+high)>>1;
if(a[mid]>k){//最后一个k在其左边
high=mid-1;
}else if(a[mid]<k){//<k最后一个k在其右边
low=mid+1;
}else{//=k
if(mid<a.length-1 && a[mid+1]==k)
low=mid+1;
else
return mid;
}
}
return -1;
}
}
39.二叉树的深度 🔺
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
递归,判断左右子树高度,返回高的一方+1。
层次遍历:辅助队列,依次遍历每一层。
1.递归:
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public int TreeDepth(TreeNode root) {
if(root==null)
return 0;
return 1+(TreeDepth(root.left)>TreeDepth(root.right)? TreeDepth(root.left):TreeDepth(root.right));
}
}
2.层次遍历:
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.Queue;
import java.util.LinkedList;
public class Solution {
public int TreeDepth(TreeNode root) {
//层次遍历 非递归
if(root==null)
return 0;
Queue<TreeNode> queue=new LinkedList<>();//存储树中结点
queue.offer(root);//存储根节点
int depth=0;//深度
int width;//宽度,即该层结点个数
int count;//当前层已遍历的结点个数
TreeNode temp=null;
while(!queue.isEmpty()){
width=queue.size();
count=0;//重置该层已遍历结点个数
while(count<width){
temp=queue.poll();//队列头结点,即该层最左结点
if(temp.left!=null){//判断左右子树是否空,非空则入队
queue.offer(temp.left);
}
if(temp.right!=null){
queue.offer(temp.right);
}
count++;//该结点已遍历
}//该层遍历完毕
depth++;//深度+1;
}
return depth;
}
}
40.平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
求深度然后计算
1.递归:
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
//平衡二叉树:是一棵空树或它的左右两个子树的高度差的绝对值不超过1,
//并且左右两个子树都是一棵平衡二叉树。
if(root==null)
return true;
//递归
int left=treeDepth(root.left);
int right=treeDepth(root.right);
int res=left-right;
if(res>1 || res <-1)
return false;
return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
public int treeDepth(TreeNode root){
if(root==null)
return 0;
int left=treeDepth(root.left);
int right=treeDepth(root.right);
return 1+(left>right? left:right);
}
}
2.从下往上遍历:
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
//平衡二叉树:是一棵空树或它的左右两个子树的高度差的绝对值不超过1,
//并且左右两个子树都是一棵平衡二叉树。
//递归 先判断左右子树,然后判断根结点,避免重复遍历 从下往上
return getDepth(root)!=-1;
}
public int getDepth(TreeNode root){
if(root==null)
return 0;
//若不平衡则返回-1
int left=getDepth(root.left);
if(left==-1)
return -1;
int right=getDepth(root.right);
if(right==-1)
return -1;
//判断左右子树高度,如果不平衡则返回-1,平衡则返回左右子树中的高度最大者+1作为该节点的深度
return Math.abs(left-right)>1 ? -1 : 1+Math.max(left,right);
}
}
如有错误,欢迎指正