题目与解题思路均来自牛客网
入门:
第一题:求斐波那契数列的第n项
public static int fibD(int n){ //递归方法
if(n == 1 || n == 2){
return 1;
}
else{
return fibD(n-1) + fibD(n - 2);
}
}
public static int fibX(int n){ //循环方法
if(n < 0){
return -1;
}
int f1 = 1;
int f2 = 1;
int f3 = 1;
for (int i = 2; i < n; i++) {
f3 = f1 + f2;
f1 = f2;
f2 = f3;
}
return f3;
}
public static void main(String[] args) {
System.out.println(fibD(10));
System.out.println(fibX(10));
}
总结:
- 建议使用循环方式,因为递归方式计算时间会很久。
第二题:给定一个m × n
大小的矩阵(m行, n列),按螺旋的顺序返回矩阵中的所有元素。
输入:
[1,2,3]
[4,5,6]
[7,8,9]
输出:[1,2,3,6,,9,8,7,4,5]
&ems; 主要是找出螺旋的规律,和边界的判别。
- 1、如果数字为空,则返回空数组
- 2、定义四个边界以及当前方向
- 3、当左边界小于等于右边界,且上边界小于等于下边界时,执行while循环:按照 左→右,上→下,右→左,下→上的顺序,依次将路径上的字符添加到结果里。
public static ArrayList<Integer> spiralOrders1(int[][] matrix) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(matrix.length==0)
return res;
if(matrix[0].length==0)
return res;
int up = 0;
int down = matrix.length-1;
int left = 0;
int right = matrix[0].length-1;
int i=0;
while(true){
if(left>right){
return res;
}
//向右
for(int k=left;k<=right;k++)
res.add(matrix[up][k]);
up++;
if(up>down){
return res;
}
//向下
for(int k=up;k<=down;k++)
res.add(matrix[k][right]);
right--;
if(left>right){
return res;
}
//向左
for(int k=right;k>=left;k--)
res.add(matrix[down][k]);
down--;
if(up>down) {
return res;
}
//向上
for(int k=down;k>=up;k--)
res.add(matrix[k][left]);
left++;
}
}
public static void main(String[] args) {
ArrayList<Integer> n = new ArrayList<Integer>();
int[][] matrix = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
System.out.println(spiralOrders1(matrix));
}
第三题:山峰元素是指其值大于等于左右相邻的元素。给定一个输入数组nums,任意两个相邻元素值不相等,数组可能包含多个山峰。找到索引最大的那个山峰元素并返回其索引。
输入:[2,4,1,2,7,8,4]
输出:5
public static int solve (int[] a) {
// write code here
int index=0;
for(int i=1;i<a.length;i++){
if(a[i-1]<a[i]){
index=i;
}
}
return index;
}
public static void main(String[] args) {
int[] b = {1,2,4,5,3,7,9,11,2,33,43,9};
int m = solve(b);
System.out.println(m);
}
- 总结:通过循环寻找峰值,即前一个值小于后一个值就是山峰。通过判断数组前一个值是否小于后一个值,如果是,则将下标值
i
赋给index
,不断更新寻找。
第四题:给定一个整型矩阵matrix,请按照顺时针转圈的方式打印它。
//转圈打印矩阵
public static ArrayList<Integer> rotationMatrix(int[][] matrix){
//第一步:new一个新的数组对象
ArrayList<Integer> rec = new ArrayList<Integer>();
//第二步:判空
if(matrix.length == 0){
return rec;
}
if(matrix[0].length == 0){
return rec;
}
//第三步:定义边界
int top = 0;
int bottom = matrix.length - 1;
int left = 0;
int right = matrix[0].length - 1;
//第四步:循环赋值
while(true){
//进行边界判别,//从左到右
if(left > right){
return rec;
}
for (int i = left; i <= right; i++) {
rec.add(matrix[top][i]);
}
top++;
//判断边界,//从上到下
if(top > bottom){
return rec;
}
for(int i = top; i <= bottom; i++){
rec.add(matrix[i][right]);
}
right--;
//判断边界,//从右到左
if(right < left){
return rec;
}
for (int i = right; i >= left ; i--) { //从右往左
rec.add(matrix[bottom][i]);
}
bottom--;
//判断边界,//从下到上
if(bottom < top){
return rec;
}
for (int i = bottom; i >= top; i--) { //从下往上 都要用 --
rec.add(matrix[i][left]);
}
left++;
}
}
public static void main(String[] args){
int[][] matrix = {{1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15}, {16,17,18,19,20}};
System.out.println(rotationMatrix(matrix));
}
- 总结:
- 此题思路与第二题思路完全相同,需要注意的的是循环条件与边界条件的选取,当从右往左时循环条件应为
right > left
且索引为i--
,从下往上时循环条件为bottom > top
索引也为i--
.
*ArrayList<Integer> rec = new ArrayList<Integer>()
此条语句为建立一个新的空数组对象,对象名为rec
简单:
第一题:给出两个有序的整数数组A和B,请将数组B合并到数组A中,变成一个有序的数组。
- 假设A和B中初始的元素数目分别为m和n,A数组有足够的空间存放B元素,即空间大小大于或等于m+n。
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
- 思路一:排序,将第二个数组直接添加到第一个数组后边然后遍历。
public static int[] concet(int[] arr1, int[] arr2, int m, int n){
int j = 0;
for (int i = m; i < m+n; i++) {
arr1[i] = arr2[j];
j++;
}
Arrays.sort(arr1);
return arr2;
}
public static void main(String[] args){
int[] arr1 = {1,2,3,0,0,0};
int m = 3;
int[] arr2 = {2,5,8};
int n = 3;
int[] output = concet(arr1,arr2,3,3);
System.out.println(Arrays.toString(output));
}
- 思路二:定义一个额外的数组来保存arr1于arr2比较后的结果,此数组的长度为
arr1.length + arr2.length
。当arr1的元素小于arr2的元素时,将arr1的元素保存到新建的数组中,同时让arr1和新建的数组下标同时向后移动(++),此时arr2的下标并未发生变化,再次进行比较。
//思路二:额外数组
public static void concet(int[] arr1, int[] arr2, int m, int n){
int j = 0;//第一个数组的指针
int k = 0;//第二个数组的指针
int[] nums = new int[m+n];
for (int i = 0; i < nums.length; i++) {
//防止越界
if (j < arr1.length && k < arr2.length) {
if (arr1[j] <= arr2[k]) {
nums[i] = arr1[j];
j++;
} else if (arr1[j] > arr2[k]) {
nums[i] = arr2[k];
k++;
}
}
//如果一个数组比较完了,即k或j大于数组长度时,直接将另一个数组剩下的元素赋给新数组剩余的空间。
else if(j >= arr1.length){
nums[i] = arr2[k];
k++;
j++;
}
else{
nums[i] = arr1[j];
j++;
k++;
}
}
for (int num: nums) {
System.out.println(num + " ");
}
}
public static void main(String[] args){
int[] arr1 = {1,2,3,4,5,6};
int m = 3;
int[] arr2 = {2,5,8};
int n = 3;
concet(arr1,arr2,3,3);
}
- 思路三:第二种思路是前插法,但需要开辟新空间,但如果想不额外开辟空间的话,使用前插法在原数组上进行处理会影响到原数组中未处理的值。所以我们使用尾插法(后入?),通过比较最大值,将两者的最大值插入到数组A的末尾。
//思路三:尾插法
public static void concet(int[] arr1,int[] arr2, int m, int n){
//m表示arr1元素的长度
//n表示arr2元素的长度
for (int i = arr1.length - 1; i >0 ; i--) {
if(m == 0){ //当arr1中的元素个数为0时,将arr2中的元素从末尾依次插入新数组(arr1数组的额外空间)
arr1[i] = arr2[n-1];
n--;
}
else if(n == 0){ //当arr2中的元素个数为0时,跳出循环。
break;
}
else if(arr1[m-1] > arr2[n-1]){ //判断两数组给自末尾元素的大小,将更大的元素插入arr1数组的末尾。
arr1[i] = arr1[m-1];
m--;
}
else{
arr1[i] = arr2[n-1];
n--;
}
}
for (int num: arr1) {
System.out.println(num + " ");
}
}
public static void main(String[] args){
int[] arr1 = {1,2,3,0,0,0};
int m = 3;
int[] arr2 = {2,5,8};
int n = 3;
concet(arr1,arr2,3,3);
}
第二题:数组中两数之和,给出一个整数数组,请在数组中找出两个加起来等于目标值的数,你给出的函数twoSum需返回这两个数字的下标(index1, index2)
,需要满足index1
小于index2
。注意:下标从1开始的。假设给出的数组中子存在唯一解。
输入:[3,2,4], 6
输出:[2,3]
- 解法一:通过两个循环嵌套来遍历所有结果,将目标结果下标返回。
public static int[] fun(int[] array, int target){
int[] ret = new int[2];
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if(array[i] + array[j] == target){
ret[0] = i;
ret[1] = j;
}
}
}
return ret;
}
public static void main(String[] args){
int[] array = {1,2,3,4,5,6};
int target = 9;
int[] output = fun(array, target);
System.out.println(Arrays.toString(output));
}
第三题:数组中相加和为0的三元组。给出一个有n个元素的数组S,S中是否有元素a,b,c满足 a+b+c=0? 找出数组S中所有满足条件的三元组。
- 注意:
- 1、三元组(a,b,c)中的元素必须按非降序排列。(即 a ≤ b ≤ c)
- 2、解集中不能包含重复的三元组
输入:[-2, 0, 1, 1, 2];
输出:[[-2, 0, 2], [-2, 1, 1]]
public static void main(String[] args){
int[] num = {-2, 0, 1, 1, 2};
System.out.println(threeSum(num));
}
public static ArrayList<ArrayList<Integer>> threeSum(int[] num){
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
if (num == null){
return result;
}
//对数组进行排序
Arrays.sort(num);
int sum,left,right;
for (int i = 0; i < num.length - 2; i++) { //因为要找出三个数,所以i只用最多固定到倒数第三个数即可
//避免重复,例如如果前面已经计算了以-1开头的,后面就不用计算了
if(i != 0 && num[i] == num[i - 1]){
continue;
}
left = i + 1;
right = num.length - 1;
/**
* 固定一个数,从后面的数中选出两个数,因为数组是有序的,所以可以
* 用两个数组下标left和right,left指向当前元素的后一个位置,right指向最后一个位置
* 三个数相加的和等于0时,加入解集
* 小于0时,把left往右边移动
* 大于0时,把right往左移动
*/
while(left < right){
sum = num[left] + num[right];
if(sum + num[i] == 0){ //如果三个数等于零,则新建一个数组用来存放这三个数
ArrayList<Integer> solution = new ArrayList<>();
solution.add(num[i]);
solution.add(num[left]);
solution.add(num[right]);
result.add(solution);
left++;
right--;
//这个优化必须加,不加时间超限,其实这个优化也没太大作用嘛
while (left < right && num[left] == num[left - 1]) {
left++;
}
while (left < right && num[right] == num[right + 1]) {
right--;
}
} else if (sum + num[i] < 0) {
left++;
} else {
right--;
}
}
}
return result;
}
第四题:缺失数字,从0,1,2,…,n这n+1个数,组成有序数列,找出这n个数中缺失的那个数。
输入:[0,1,2,3,4,5,7]
输出:6
- 拙略的for循环,虽然可以解题,但明显不是最佳解
public static void main(String[] args){
int[] array = {3,4,5,6,7,9,10};
int[] array1 = {18,19,20,22,23,24,25};
int output = findNumber(array1);
System.out.println(output);
}
public static int findNumber(int[] array){
int j = array[0];
for (int i = 0; i < array.length; i++) {
if(array[i] != j){
return j;
}
j++;
}
return -1;
}
第五题:寻找数组中未出现的最小正
public static int findMinNumber(int[] array){
if (array.length == 0){
return 1;
}
Arrays.sort(array);
int len = array.length;
if(array[len-1] < 0){
return 1;
}
int min = 1;
for (int i = 0; i < len; i++) {
if(min == array[i]){
min++;
}
}
return min;
}
public static void main(String[] args){
int[] array = {-1,-4,-7,3,7,9,4,1,2,6,8,9,45,34,23,-10};
System.out.println(findMinNumber(array));
}