精选算法入门——day2
- 题目一
- 题干
- 解题思路一
- 解题思路二
- 解题思路三
- 思路三代码
- 题目二
- 题干
- 解题思路
- 代码
- 题目三
- 题干
- 解题思路一
- 代码
- 解题思路二
- 代码
- 解题思路三
- 代码
- 题目四
- 题干
- 解题思路
- 代码
题目一
题干
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组
{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解题思路一
拿到这条题目自然想到统计每个数字,通过每个数字出现的次数来判断是否超过数组长度的一半,那么我们可以借用map这个数据结构,所谓的map就是键值对,键就是数组中的元素,值就是每个元素出现的次数,通过遍历整个数组将每个数组中的元素记录下来,最后比较每个元素出现的次数与数组长度一般的关系。这种方法当然可以,但是我们借用了map,不免会造成更多的空间复杂度。
解题思路二
我们可以先进行排序,那么如果真的存在超过数组长度的数,那么一定是在数组的中央,比如:
从图中我们可以看到,如果从最左边开始就是目标值,那么中间位置就是我们要找的数字(第一个图);如果是最右边开始就是目标值,那么中间位置也是我们要找的数字(第二个图);如果从中间某个位置开始,那么中间位置也是我们要找的数字(第三个图);但是并不是中间数就是一定满足条件的,比如,第四个图,虽然在中间位置,但是并不符合要求,所以我们找到中间位置后,通过遍历一遍数组对中间数字出现的个数进行统计,统计完在与数组长度的一半进行对比,如果大于则符合要求,如果小于则不符合要求。
解题思路三
目标条件:目标数据超过数组长度的一半。那么对数组,我们同时去掉两个不同的数字,到最后剩下的一个数就是 该数字。如果剩下两个,那么这两个也是一样的,就是结果,在其基础上把最后剩下的一个数字或者两个回到原来数组中, 将数组遍历一遍统计一下数字出现次数进行最终判断。那么具体怎么做呢?我们可以先将第一个数字记下来当作目标值(target),并把他出现的个数(times)记为1,然后向后遍历,如果出现与target相同的数字那么就将times+1,如果出现不同的数字就将times-1,当times出现为0时就将下一个数组当作target,并将times更新为1。在遍历完这个数组的时候,target就是出现次数最多的值(PS:但不一定是目标值),因此我们需要对整个数组进行遍历,对target出现次数进行统计。我们看具体的例子:
首先我们将1记为target,并把times记为1,向后遍历后发现2与target(1)不同,那么times-1,此时times为0,那么把后面一个数字(3)记为target,并把times记为1,再向后遍历,发现2与现在的target(3)不同,那么times-1,此时times为0,再把后面一个数字(2)记为target,并把times记为1,向后遍历,发现与target(2)相同,那么times+1,此时times为2,继续向后遍历,后面出现分别是5和4,times变为了0,此时将最后一个数字2记为target,times记为1,遍历结束,这里target就是此数组中出现次数最多的出现,这时候在进行一次遍历,将2出现的个数统计出来,并于数组长度的二分之一进行比较,得出结果,数字2就是我们要找的目标数值。
思路三代码
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
public int MoreThanHalfNum_Solution (int[] numbers) {
// write code here
if (numbers == null || numbers.length == 0){
return 0;
}
int target = numbers[0];
int times = 1;
for(int i = 1; i < numbers.length; i++){
if(times==0){
target = numbers[i];
times = 1;
}
if(target == numbers[i]){
times++;
}
else{
times--;
}
}
times = 0;
for(int i = 0;i<numbers.length;i++){
if(target == numbers[i]){
times++;
}
}
return times > (numbers.length/2)? target:0;
}
}
题目二
题干
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字
符串为We%20Are%20Happy。
解题思路
这条题目还是比较简单的,我们可以看到空格被替换成了“%20”,也就是说一个字符(空格是一个字符),被替换成了三个字符(‘%’,‘2’,‘0’),那么我们可以先统计空格的个数,通过空格的个数计算出新的字符串长度(老字符串长度+2×空格的个数),然后一个指针指向新字符串的最后一个位置,一个指针指向老字符串的最后一个位置,两个指针同时先前遍历,遍历过程中也将老字符串指针指向的字符赋值给新字符串指针,当老字符串指向空格时,把他替换成“%20”(PS:这里需要注意,因为是从后向前遍历,并且是一个一个的赋值,那么应该是先赋值“0”,然后是“2”,最后时“%”),直到两个指针完成遍历。这个题目我就不在画图了,直接上代码。
代码
public class Solution {
public static String replaceSpace(StringBuffer str) {
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == ' ') {
count++;
}
}
int old_end = str.length() - 1;
int new_length = str.length() + 2 * count;
int new_end = new_length - 1;
str.setLength(new_length);
while (new_length >= 0 && old_end >= 0) {
if (str.charAt(old_end) == ' ') {
str.setCharAt(new_end, '0');
new_end--;
str.setCharAt(new_end, '2');
new_end--;
str.setCharAt(new_end, '%');
new_end--;
old_end--;
} else {
str.setCharAt(new_end,str.charAt(old_end));
new_end--;
old_end--;
}
}
return str.toString();
}
public static void main(String[] args) {
StringBuffer str = new StringBuffer("We Are Happy.");;
System.out.println(replaceSpace(str));
}
}
题目三
题干
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
解题思路一
这个题目是一个非常典型的题目,有很多种方法,在这我们介绍三种,第一种,引入栈,我们知道栈是先进后出,通过将遍历的每个元素按顺序放入栈中,在把每个栈中的元素弹出来,那么就能够完成,这里也不再举具体的例子了,直接上代码。
代码
import java.util.*;
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
if(listNode == null){
return list;
}
Stack<Integer> stack = new Stack<>();
while(listNode != null){
stack.add(listNode.val);
listNode = listNode.next;
}
while(!stack.isEmpty()){
list.add(stack.pop());
}
return list;
}
}
解题思路二
我们在原来遇到数组逆置的时候是通过双指针进行逆置,也就是说前一个指针跟后一个指针所指向的内容进行交换,前指针往后,后指针往前,直到两个指针相碰。但是单链表不行,原因就在于所谓的后指针无法往前移动,前面的节点可以往后找,但是后面的节点无法往前找。因此我们自然就想到一个思路,我们先将链表转化为数组,然后利用数组进行逆置。这个我们也不举例子了,直接上代码。
代码
import java.util.*;
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
if(listNode == null){
return list;
}
while(listNode!=null){
list.add(listNode.val);
listNode = listNode.next;
}
int i = 0;
int j = list.size() - 1;
while(i < j){
int tmp = list.get(i);
list.set(i,list.get(j));
list.set(j,tmp);
i++;
j--;
}
return list;
}
}
解题思路三
我们可以采用递归的方式来解决这个问题。我们假设有一个链表,我们要逆序打印每个节点,那么第一个节点是最后一个打印,也就意味着我要打印第一个,那么我要把后面所有顺序的节点都打印了,而第二个节点我要打印,也就是除了第一个节点把后面所有顺序的节点都打印了,以此类推,那么到最后一个节点,也就是最先打印节点,那么很明显这个是可以看作一个递归问题的。也就是一直往下递归,递归到最后一个节点,打印,然后出递归,打印……我们用例子理解一下。
比如现在我们要逆序打印:1 2 3,那么我们先进入第一层递归(1),我们发现1后面还有节点,那么进入第二层递归(2),我们发现2后面还有节点,那么进入第三层递归(3),我们发现后面没用节点了,那么就打印该节点(3),出递归,打印节点(2),出递归,打印节点(1),递归结束。如此一来打印的顺序就是3,2,1。这就是用递归解决这道问题的思路,我们上代码。
代码
import java.util.*;
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
public void function (ListNode listNode,ArrayList<Integer> list){
if(listNode == null){
return;
}
function(listNode.next,list);
list.add(listNode.val);
}
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
function(listNode,list);
return list;
}
}
题目四
题干
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
解题思路
这条题目难度就相对于上面几道题就会难上许多,首先我们要先了解如何通过前序和后序遍历手写出二叉树。我们举个例子:
我们复习回顾一下二叉树的遍历方式:
前序遍历:根→左子树→右子树
中序遍历:左子树→根→右子树
后续遍历:左子树→右子树→根
由此我们可以知道前序遍历中第一个数字就是整个树中的根节点,那么在中序遍历中,就可以通过根分为左右两边,如图所示:
从图中我们可以看到根节点把原始的中序变为了左子树的中序和右子树的中序,而在前序当中,也有对应左子树的前序和对应右子树的前序,如图所示:
那么我们又可以根据左子树的前序和中序(前序:2485;中序:8425),以及右子树的前序和中序(前序:367;中序:637)进行进一步划分,以此类推,直到划分完,因此我们仍然可以用递归的方法来解决这个问题,也就是说首先利用前序的第一个节点划分中序的左右子树,根据左右子树的中序节点的个数,来确定前序遍历中对应的左右子树的前序节点的个数,那么我们就把求一整棵树的问题划分求左右子树的问题(可能有些拗口,可以对照着上面的图来理解)。我们来看代码的实现。
代码
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param preOrder int整型一维数组
* @param vinOrder int整型一维数组
* @return TreeNode类
*/
public TreeNode function(int[] preOrder,int pre_start,int pre_end, int[] vinOrder,int vin_start, int vin_end){
if(pre_end<pre_start||vin_end<vin_start){
return null;
}
TreeNode root = new TreeNode(preOrder[pre_start]);
for(int i = vin_start;i <= vin_end;i++){
if(preOrder[pre_start] == vinOrder[i]){
root.left = function(preOrder,pre_start+1,pre_start+i-vin_start,vinOrder,vin_start,i-1);
root.right = function(preOrder,pre_start+i-vin_start+1,pre_end,vinOrder,i+1,vin_end);
break;
}
}
return root;
}
public TreeNode reConstructBinaryTree (int[] preOrder, int[] vinOrder) {
// write code here
if(preOrder.length==0||vinOrder.length==0||preOrder.length!=vinOrder.length){
return null;
}
return function(preOrder,0,preOrder.length-1,vinOrder,0,vinOrder.length-1);
}
}