目录
1.数组中重复的数字
描述:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
数据范围:0\le n \le 10000 \0≤n≤10000
进阶:时间复杂度O(n)\O(n) ,空间复杂度O(n)\O(n)
做题思路:重头到尾扫描数组S中的每一个元素,当扫描到第i个元素的时候,比较第i个元素位置的值m是否等于i,如果相等,则说明该元素已经在排好序的位置,继续扫描其他元素;如果不相等,先判断m是否等于S[m],相等则说明不同位置上的元素值相等,即元素重复。直接返回元素;否则交换m和S[m]将他们放置到排好序的位置。
图示:
代码:
public void swap(int[] numbers,int a,int b){
int t=numbers[a];
numbers[a]=numbers[b];
numbers[b]=t;
}
public int duplicate (int[] numbers) {
int i=0,n=numbers.length;
while(i<n){
if(numbers[i]==i){
i++;
continue;
}else {
if(numbers[i]==numbers[numbers[i]]){
return numbers[i];
}
else
swap(numbers,i,numbers[i]);
}
}
return -1;
}
2.二维数组中的查找
描述:
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
数据范围:矩阵的长宽满足 0 \le n,m \le 5000≤n,m≤500 , 矩阵中的值满足 0 \le val \le 10^90≤val≤109
进阶:空间复杂度 O(1)O(1) ,时间复杂度 O(n+m)O(n+m)
做题思路:首先看四个角,左上与右下必定为最小值与最大值,而左下与右上就有规律了:左下元素大于它上方的元素,小于它右方的元素,右上元素与之相反。既然左下角元素有这么一种规律,相当于将要查找的部分分成了一个大区间和小区间,每次与左下角元素比较,我们就知道目标值应该在哪部分中。
具体做法:
1.首先获取矩阵的两个边长,判断特殊情况。
2.首先以左下角为起点,若是它小于目标元素,则往右移动去找大的,若是他大于目标元素,则往上移动去找小的。
3.若是移动到了矩阵边界也没找到,说明矩阵中不存在目标值。
代码:
public class Solution {
public boolean Find(int target, int [][] array) {
if(array.length==0||array[0].length==0)
return false;
int n=array.length;
int m=array[0].length;
for(int i=n-1,j=0;i>=0&&j<m;){
if(array[i][j]>target){
i--;
}else if(array[i][j]<target){
j++;
}else
return true;
}
return false;
}
}
3.替换字符串中的空格
描述:
请实现一个函数,将一个字符串s中的每个空格替换成“%20”。
例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
数据范围:0 \le len(s) \le 1000 \0≤len(s)≤1000 。保证字符串中的字符为大写英文字母、小写英文字母和空格中的一种。
做题思路:
①创建一个长度为3*n的数组,对字符串中的每个字符进行遍历,进行判断,如果碰到字符是空格,我们就将空格那个i位置变成%,2与0分别占到i+1与i+2。如果不是空格,就将字符按照数组的位置依次存储下去。直至将字符串完全遍历结束。最后将数组输出得到的就是答案。
代码:
public String replaceSpace(String s) {
int length = s.length();
char[] array = new char[length * 3];
int index = 0;
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c == ' ') {
array[index++] = '%';
array[index++] = '2';
array[index++] = '0';
} else {
array[index++] = c;
}
}
String newStr = new String(array, 0, index);
return newStr;
}
4.从尾到头打印链表
描述
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入{1,2,3}的链表如下图:
返回一个数组为[3,2,1]
0 <= 链表长度 <= 10000
解题思路:
①非递归
listNode 是链表,只能从头遍历到尾,但是输出却要求从尾到头,这是典型的"先进后出",我们可以想到栈!
ArrayList 中有个方法是 add(index,value),可以指定 index 位置插入 value 值,所以我们在遍历 listNode 的同时将每个遇到的值插入到 list 的 0 位置,最后输出 list 即可得到逆序链表。
代码:
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<Integer>();
ListNode tmp=listNode;
while(tmp!=null){
list.add(0,tmp.val);
tmp=tmp.next;
}
return list;
}
}
②递归
代码:
public class Solution {
ArrayList<Integer> list=new ArrayList();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode!=null){
printListFromTailToHead(listNode.next);
list.add(listNode.val);
}
return list;
}
}
5.斐波那契数列
描述:
大家都知道斐波那契数列,现在要求输入一个正整数 n ,请你输出斐波那契数列的第 n 项。
斐波那契数列是一个满足 fib(x)=
的数列
数据范围:1\leq n\leq 401≤n≤40
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n) ,本题也有时间复杂度 O(logn)O(logn) 的解法
解题思路:
①非递归
根据题目可知,第一项与第二项都是1,所以首先判断n的值,当n=1或n=2时,直接返回1,当n>3时,定义一个变量sum,用于存储前两项的和,求完一次,将a与b的值变成b与sum的值,重复此过程,直至求至n项,将sum返回。
代码:
public class Solution {
public int Fibonacci(int n) {
if(n==1||n==2){
return 1;
}else{
int sum=0,a=1,b=1;
for(int i=3;i<=n;i++){
sum=a+b;
a=b;
b=sum;
}
return sum;
}
}
}
②递归
我们可以根据公式倒推,因为F(n)=F(n−1)+F(n−2),而F(n−1)与F(n−2)又可以作为子问题继续计算,因此可以使用递归。
终止条件: 当递归到数列第1项或是第0项的时候,可以直接返回数字。
返回值: 返回这一级子问题的数列值。
本级任务: 获取本级数列值:即前两项相加。
具体做法:
step 1:低于2项的数列,直接返回n。
step 2:对于当前n,递归调用函数计算两个子问题相加。
代码:
public class Solution {
public int Fibonacci(int n) {
//从0开始,第0项是0,第一项是1
if (n <= 1)
return n;
else{
//根据公式递归调用函数
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
}
}
6.旋转数组的最小数字
描述:
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1 ≤ n ≤ 10000,数组中任意元素的值:0 ≤ val ≤ 10000
要求:空间复杂度:O(1) ,时间复杂度:O(logn)
解题思路:
①二分法
旋转数组将原本有序的数组分成了两部分有序的数组,因为在原始有序数组中,最小的元素一定是在首位,旋转后无序的点就是最小的数字。我们可以将旋转前的前半段命名为A,旋转后的前半段命名为B,旋转数组即将AB变成了BA,A部分和B部分都是各自有序的,每次比较中间值,确认目标值(最小元素)所在的区间。
1:双指针指向旋转后数组的首尾,作为区间端点。
2:若是区间中点值大于区间右界值,则最小的数字一定在中点右边。
3:若是区间中点值等于区间右界值,则是不容易分辨最小数字在哪半个区间,比如[1,1,1,0,1],应该逐个缩减右界。
4:若是区间中点值小于区间右界值,则最小的数字一定在中点左边。
5:通过调整区间最后即可锁定最小值所在。
代码:
public class Solution {
public int minNumberInRotateArray(int [] array) {
int left=0;
int right=array.length-1;
while(left<right){
int mid=(left+right)/2;
if(array[mid]>array[right]){
left=mid+1;
}else if(array[mid]<array[right]){
right=mid;
}else{
right--;
}
}
return array[left];
}
}
②遍历数组
设置一个变量res为array[0],从左到右遍历整个数组,依次检查当前元素与记录元素的大小,若是当前元素更小,则记录最小的元素更新。遍历结束后,记录的元素则为数组的最小数字。
代码:
public class Solution {
public int minNumberInRotateArray(int [] array) {
int res = array[0];
for(int i = 1; i < array.length; i++)
res = Math.min(res, array[i]);
return res;
}
}
7.二进制中‘1’的个数
描述:
输入一个整数 n ,输出该数32位二进制表示中1的个数。其中负数用补码表示。
数据范围:- 2^{31} <= n <= 2^{31}-1−231<=n<=231−1
即范围为:-2147483648<= n <= 2147483647−2147483648<=n<=2147483647
本题知识点:位运算
1.按位与运算符( & )
运算规则:只有两个数的二进制同时为1,结果才为1,否则为0。(负数按补码形式参加按位与运算)
举例:3 &5 即 00000011 & 00000101 = 00000001 ,所以 3 & 5的值为1。
2.按位或运算符( | )
运算规则:参加运算的两个数只要两个数中的一个为1,结果就为1。
即 0 | 0= 0 , 1 | 0= 1 , 0 | 1= 1 , 1 | 1= 1 。
举例:2 | 4 即 00000010 | 00000100 = 00000110 ,所以2 | 4的值为 6
3.异或运算符( ^ )
运算规则:参加运算的两个数,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
即 0 ^ 0=0 , 0 ^ 1= 1 , 1 ^ 0= 1 , 1 ^ 1= 0 。
举例: 2 ^ 4 即 00000010 ^ 00000100 =00000110 ,所以 2 ^ 4 的值为6 。
4.<<运算符( << )
运算规则:n << i = n * 2^i 。(箭头指向左边 ‘乘’ )
举例:18 << 2 = 18 * 2^2 =72,18 << 3 = 18 * 2^3 = 144
5.>>运算符( >> )
运算规则:n << i = n / 2^i 。(箭头指向右边 ‘除’ ,除不尽有余数向下取整)
举例:18 >> 2 = 18 / 2^2 = 4,18 >> 3 = 18 / 2^3 = 2
解题思路:
移位运算,每次移动一位就可以。至于怎么统计到1呢?我们都只知道数字1与数字相位与运算,其实只是最后一位为1就是1,最后一位为0就是0,这样我们只需要将数字1移位运算,就可以遍历二进制的每一位,再去做位与运算,结果为1的就是二进制中为1的。
1:遍历二进制的32位,通过移位0-31次实现。
2:将移位后的1与数字进行位与运算,结果为1就记录一次。
代码:
public class Solution {
public int NumberOf1(int n) {
int count=0;
for(int i=0;i<32;i++){
if((n&(1 << i))!=0){
count++;
}
}
return count;
}
}
8.打印从1到最大的n位数
描述
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
1. 用返回一个整数列表来代替打印
2. n 为正整数,0 < n <= 5
解题思路:首先我们需要算出n位数的最大值,也就是10^n-1,根据这个,我们只需要从1遍历到10^n-1,创建一个长度为10^n-1的数组,将其一一存入后,将数组返回即可。
代码:
public class Solution {
public int[] printNumbers (int n) {
int sum=1;
for(int i=0;i<n;i++){
sum*=10;
}
int[] arr=new int[sum-1];
for(int i=1;i<sum;i++){
arr[i-1]=i;
}
return arr;
}
}
9.删除链表的节点
描述:
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
1.此题对比原题有改动
2.题目保证链表中节点的值互不相同
3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
数据范围:
0<=链表节点值<=10000
0<=链表长度<=10000
思路分析:
1.首先判断该单链表是否为空,如果为空直接返回null
2.当链表不为空时,那我们就从头结点开始遍历单链表
3.因为头节点不能动,所以我定义一个辅助指针指向头节点,用于遍历单链表
4.从头节点开始遍历单链表
4.1根据temp.next.val=val,找到对应节点,temp.next=temp.next.next
4.2直到链表最后,仍然没有找到对应节点,返回null
示意图:
代码:
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @param val int整型
* @return ListNode类
*/
public ListNode deleteNode (ListNode head, int val) {
if(head == null){
return null;
}
ListNode temp=head;
if(head.val==val){
head=head.next;
return head;
}
while(temp.next!=null){
if(temp.next.val == val){
temp.next=temp.next.next;
break;
}
temp=temp.next;
}
return head;
}
}
10.链表中倒数最后k个结点
描述:
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
数据范围:0 <= n <= 10^5,0 <= ai<= 10^9,0 <= k<= 10^9
例如输入{1,2,3,4,5},2时,对应的链表结构如下图所示:
其中蓝色部分为该链表的最后2个结点,所以返回倒数第2个结点(也即结点值为4的结点)即可,系统会打印后面所有的节点来比较。
思路分析:
1.首先我们需要知道该单链表的长度,定义一个辅助变量temp指向head,从头到尾遍历链表,temp每往后挪动一次,length++,直到最后temp指向最后一个节点,得到链表的长度length
2.由于单链表只能从头到尾遍历,所以我们需要计算temp从头节点到倒数第k个节点需要挪动length-k次,返回此时的temp,即是k节点后的所有节点
代码:
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode FindKthToTail (ListNode pHead, int k) {
int length=1;
if(pHead == null||k == 0){
return null;
}
ListNode temp=pHead;
while(temp.next != null){
temp=temp.next;
length++;
}
if(k > length){
return null;
}
temp=pHead;
int num=length-k;//5-2=3
for(int i=1 ;i < num; i++){//1 2
temp=temp.next;
}
if(k == length){
return temp;
}
return temp.next;
}
}