剑指Offer刷题整理(21-30),Java版

题目来自牛客网

21.栈的入栈出栈序列

题目描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

思路:

新建一个栈来验证即可

算法实现

class Solution21{
	 public boolean IsPopOrder(int [] pushA,int [] popA) {
	      int len=pushA.length;
	        Stack <Integer> stack=new Stack<>();
	        int i=0, j=0;
	        stack.push(pushA[j++]);
	        while(!(i==len&&j==len))
	        {
	            if(popA[i]==stack.peek())
	            {
	                stack.pop();
	                i++;
	            }
	            else if(j<len)
	            {
	                stack.push(pushA[j++]);
	            }
	            else
	                return false;
	        }
	       return true;
	    }
}

22.从上往下打印二叉树

题目描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

思路:

层次遍历,使用队列

算法实现

class Solution22 {
	 public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
	        Queue<TreeNode> queue = new LinkedList<TreeNode>();
	        ArrayList<Integer> list =new ArrayList<>();
	        if(root==null){//使用队列进行层次遍历
	            return list;
	        }
	        queue.offer(root);
	        while(!queue.isEmpty()){
	            TreeNode tr =queue.poll();
	            list.add(tr.val);
	            if(tr.left!=null){
	                queue.offer(tr.left);
	            }
	             if(tr.right!=null){
	                queue.offer(tr.right);
	            }
	        }
	        return list;
	    }
}

23.二叉搜索树的后序遍历序列

题目描述

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

思路:

后序遍历序列的最后一个元素为二叉树的根节点;.二叉搜索树左子树上所有的结点均小于根结点、右子树所有的结点均大于根结点。

算法实现

算法步骤如下:

  1. 找到根结点;
  2. 遍历序列,找到第一个大于等于根结点的元素i,则i左侧为左子树、i右侧为右子树;
  3. 我们已经知道i左侧所有元素均小于根结点,那么再依次遍历右侧,看是否所有元素均大于根结点;若出现小于根结点的元素,则直接返回false;
  4. 若右侧全都大于根结点,则: 分别递归判断左/右子序列是否为后序序列;
class Solution23 {
	public boolean VerifySquenceOfBST(int[] sequence) {
		int len = sequence.length;
		if (len == 0) {
			return false;
		}
		if (len == 1) {
			return true;
		}
		return IsPostOrder(sequence, 0, len - 1);
	}

	private boolean IsPostOrder(int[] s, int l1, int l2) {
		if (l1 >= l2) {
			return true;
		}
		int i = l1;
		while (s[i] < s[l2]) {// 找到第一个大于根节点的节点
			i++;
		}
		int j = i;
		while (j < l2) {
			if (s[j] < s[l2]) {
				return false;
			}
			j++;
		}

		return IsPostOrder(s, l1, i - 1) && IsPostOrder(s, i, l2 - 1);
	}
}

24.二叉树中和为某一值的路径

题目描述

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

思路:

正常遍历二叉树,访问一个节点就将其加入到list2中,向下遍历的时候target-root.val,反之则加并且移除该节点。若target==root.val并且没有左右子树时new一个List添加到list中。

算法实现

class Solution24 {
	ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>(); 
	ArrayList<Integer> list2 = new ArrayList<Integer>();
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
    	if(root==null) {
    		return list;
    	}
    	list2.add(root.val);
    	if(root.val==target&&root.left==null&&root.right==null) {
    		list.add(new ArrayList<Integer>(list2)); 		
    	}
    	if(root.left!=null) {
    		FindPath(root.left, target-root.val);
    	}
    	if(root.right!=null) {
    		FindPath(root.right, target-root.val);
    	}
    	list2.remove(list2.size()-1);
    	target+=root.val;
    	return list;
    }

}

25.复杂链表的复制

题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

思路:

1.将原链表复制一个节点添加到原来节点的后边,即原链表ABCD->AA’BB’CC’DD’
2.对复制后的链表添加随机连接
3.断链将新链表取出来

算法实现

class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
class Solution25 {
    public RandomListNode Clone(RandomListNode pHead)
    {
    	if(pHead==null) {
    		return null;
    	}
    	//第一步,复制
    	RandomListNode p =pHead;
    	while(p!=null) {
    		RandomListNode q = new RandomListNode(p.label);
    		q.next=p.next;
    		p.next=q;
    		p=q.next;
    	}
    	//第二步,连接
    	p =pHead;
    	while(p!=null) {
    		RandomListNode q=p.next;
    		if(p.random!=null) {
    			q.random=p.random.next;
    		}
    		p=q.next;
    	}
    	//第三步,断链
    	p =pHead;
    	RandomListNode head=p.next;

    	while(p!=null) {
    		RandomListNode q=p.next;
    		if(q.next!=null) {
        		p.next=q.next;
        		p=p.next;
    			q.next=p.next;
    		}
    		if(q.next==null) {
    			p.next=null;
    			p=p.next;
    		}
    	}
        return head;
    }
}

26.二叉搜索树与双向链表

题目描述

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

思路:

使用中序遍历,中序遍历的第一个不为空的节点为头结点,然后依次遍历连接即可

算法实现

class Solution26 {
	TreeNode head=null;TreeNode q=null;
    public TreeNode Convert(TreeNode p) {

    	if(p==null) {
    		return null;
    	}
    	if(p.left!=null) {
    		Convert(p.left);
    	}
    	if(head==null) {//寻找头结点
    		head=p;
    		q=head;
    	}else {
    		p.left=q;
    		q.right=p;
    		q=p;
    	}
    	if(p.right!=null) {
    		Convert(p.right);
    	}
		return head;
    }
}

27.字符串的排列

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

思路:

递归操作,第一个数和后边的数交换,然后第二个数和后边的数交换,插入的时候查重,返回之前排序
推广:全排列问题可以借用此思路,逐一替换然后插入时判断

算法实现

class Solution27 {
    public ArrayList<String> Permutation(String str) {
       ArrayList<String> list = new ArrayList<String>();
       if(str!=null) {
    	   char[] a=str.toCharArray();
    	   SortOfString(list,a,0);
    	   Collections.sort(list);
       }
       return list;
    }

	private void SortOfString(ArrayList<String> list, char[] a, int begin) {
		if(begin==a.length-1) {
			String s=new String(a);
			if(!list.contains(s)) {	
				list.add(s);
			}
			return ;
		}
		for(int i =begin;i<a.length;i++) {
			char temp=a[i];
			a[i]=a[begin];
			a[begin]=temp;
			SortOfString(list, a, begin+1);
			temp=a[i];
			a[i]=a[begin];
			a[begin]=temp;
		}
	}


}

28.数组中出现次数超过一半的数字

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

思路:

记录第一个数为most,count为1,遍历数组遇到和most相等的数count+1,反之count-1。当count为0时,将most替换为当前元素,继续遍历,那么最后一个数则有可能是次数大于数组长度的一半的数,然后在遍历一次数组,确认是否正确

算法实现

class Solution28 {
    public int MoreThanHalfNum_Solution(int [] array) {
    	int most=array[0];
    	int count=1;
        for(int i = 1;i< array.length;i++) {
        	if(array[i]==most) {
        		count++;
        	}else {
        		count--;
        		if(count<=0) {
        			most=array[i];
        		}
        	}
        }
        int num=0;
        for(int i=0;i<array.length;i++) {
        	if(array[i]==most) {
        		num++;
        	}
        }
        if(num>array.length/2) {
        	return most;
        }else {
        	return 0;
        }
    }
}

29.最小的k个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

思路:

1.堆排序 2.partition思想的快排

算法实现

方法一:

class Solution29_1 {
	public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
		ArrayList<Integer> leastNumbers = new ArrayList<Integer>();
		while (input == null || k <= 0 || k > input.length)
			return leastNumbers;
		int[] numbers = new int[k]; // 用于放最小的k个数
		for (int i = 0; i < k; i++)
			numbers[i] = input[i];// 先放入前k个数
		for (int i = k / 2 - 1; i >= 0; i--) {
			adjustHeap(numbers, i, k - 1);// 将数组构造成最大堆形式
		}
		for (int i = k; i < input.length; i++) {
			if (input[i] < numbers[0]) { // 存在更小的数字时
				numbers[0] = input[i];
				adjustHeap(numbers, 0, k - 1);// 重新调整最大堆
			}
		}
		for (int n : numbers)
			leastNumbers.add(n);
		return leastNumbers;
	}

	// 最大堆的调整方法,
	private void adjustHeap(int[] arr, int start, int end) {
		int temp = arr[start];
		int child = start * 2 + 1;
		while (child <= end) {
			if (child + 1 <= end && arr[child + 1] > arr[child])
				child++;
			if (arr[child] < temp)
				break;
			arr[start] = arr[child];
			start = child;
			child = child * 2 + 1;
		}
		arr[start] = temp;
	}
}

方法二:

class Solution29_2 {
	public ArrayList GetLeastNumbers_Solution(int[] input, int k) {
		ArrayList aList = new ArrayList();
		if (input.length == 0 || k > input.length || k <= 0)
			return aList;
		int low = 0;
		int high = input.length - 1;
		int index = Partition(input, k, low, high);
		while (index != k - 1) {
			if (index > k - 1) {
				high = index - 1;
				index = Partition(input, k, low, high);
			} else {
				low = index + 1;
				index = Partition(input, k, low, high);
			}
		}
		for (int i = 0; i < k; i++)
			aList.add(input[i]);
		return aList;
	}

	int Partition(int[] input, int k, int low, int high) {
		int pivotkey = input[k - 1];
		swap(input, k - 1, low);
		while (low < high) {
			while (low < high && input[high] >= pivotkey)
				high--;
			swap(input, low, high);
			while (low < high && input[low] <= pivotkey)
				low++;
			swap(input, low, high);
		}
		return low;
	}

	private void swap(int[] input, int low, int high) {
		int temp = input[high];
		input[high] = input[low];
		input[low] = temp;
	}
}

30.连续子数组的最大和

题目描述

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

思路:

两种解法:
1.直接法
sum记录当前和,maxsum记录最大和,当sum+当前值小于当前值的时候丢弃之前的和,从i开始记录
2.动态规划

算法实现

方法一:

class Solution30_1 {
    public int FindGreatestSumOfSubArray(int[] array) {
        int sum=array[0];int maxsum=sum;
        if(array==null||array.length==0) {
        	return 0;
        }
        if(array.length==1) {
        	return array[0];
        }
        for(int i=1;i<array.length;i++) {
        	if(sum+array[i]<array[i]) {//考虑一种情况即可,如果sum+当前值小于i的值,丢弃前边的序列,从i开始计算
        		sum=array[i];
        	}else {
        		sum+=array[i];
        	}
        	if(sum>maxsum) {
    			maxsum=sum;
    		}
        }
        return maxsum;
    }
}

方法二:

class Solution30_2 {//DP
	/**
		动态规划
		F(i):以array[i]为末尾元素的子数组的和的最大值,子数组的元素的相对位置不变
		F(i)=max(F(i-1)+array[i] , array[i])
		res:所有子数组的和的最大值
		res=max(res,F(i)) 
	 */
    public int FindGreatestSumOfSubArray(int[] array) {
    	int max=array[0];
    	int res=array[0];
    	for(int i =1;i < array.length;i++ ) {
    		max=Math.max(max+array[i], array[i]);
    		res=Math.max(res, max);
    	}
    	return res;       
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值