1.打印从1到最大的n位数
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
1. 用返回一个整数列表来代替打印
2. n 为正整数,0 < n <= 5
思路:输入3,则最大999;输入2,则最大99;输入1,最大9(输入0也是)
返回整数列表,所以用数组
public class Main {
public static void main(String []args) {
Scanner input = new Scanner(System.in);
int n=input.nextInt();
printNumbers (n);
}
public static void printNumbers (int n) {
int size = 0;
for(int i =0;i <n;i ++){ size = 10 * size +9; }
int[] array = new int[size];
for(int i=0;i<array.length;i++){
array[i]=i+1;
System.out.println(array[i]);
}
}
}
2.剪绳子(进阶版)
长度为 n 的绳子剪成 m 段( m 、 n 都是整数, n > 1 并且 m > 1 , m <= n ),每段绳子的长度记为 k[1],...,k[m] 。请问 k[1]*k[2]*...*k[m] 可能的最大乘积是多少?
由于答案过大,请对 998244353 取模。
数据范围:2\leq n \leq 10^{14}2≤n≤1014
进阶:空间复杂度 O(1)O(1) , 时间复杂度 O(logn)O(logn)
思路:均值不等式,均分最大,求导方法得出奇异点在e,通过计算得出3最大,所以尽可能每段为3。当最后=1时,从前面拿出一段再2*2;当最后为2,直接乘上
1.暴力法
public class Solution {
public long cutRope (long number) {
if(number<=3) return number-1;
long res=1;
while(number>4){
res=res*3;
res=res%998244353;
number=number-3;
}
return res*number%998244353;
}
}
2.快速幂
思路: 1.若num>0且是偶数,则基底变平方;幂指数/2()
if(num%2==0) base = base * base % mod;
num >>= 1;
if(num%2==1){ res = res * base % mod;}(奇数停止,不是1)
2.若奇数,
if(num%2==1){ res = res * base % mod;}只乘了一个base,res=base罢了
base^2
num>>=1奇数这里的关键在base
(a^2)^(b/2) b为偶数
(a^2)^(b/2)*a b为奇数
//若num为奇数则res=base*res%mod;(单独的a被乘上,且这里的base)
// base=base*base;//这里base在以平方级增长
//num>>=1;
public class Solution {
long mod = 998244353;
public long cutRope (long number) {
if(number<=3) return number-1;
long a = number/3;//8...2
long b = number%3;//8...2
if(b==0) return Pow(3, a) % mod;
else if(b==1) return Pow(3, a-1) * 4 % mod;
else return Pow(3, a) * 2 % mod;
}
//开始快速幂,快速乘积
public long Pow(long base, long num){
long res = 1;
while(num > 0){
if(num%2==1){ res = res * base % mod;}
base = base * base % mod;
num >>= 1;
}
return res;
}}
3.调整数组顺序使奇数位于偶数前面(二)
要求:时间复杂度 O(n),空间复杂度 O(1)
1)复制法:返回函数的数组不占空间
//准备一个数组复制法
public class Solution {
public int[] reOrderArrayTwo (int[] array) {
int n=array.length-1;
int m=0;
int[] ch=new int[array.length];
for(int i=0;i<array.length;i++){
if(array[i]%2==0){
ch[n]=array[i];
n=n-1;
}else{
ch[m]=array[i];
m++;}
}
return ch;}
}
2)双指针法
思路:左边奇数指针,右边偶数指针
//左边指针没有遇到偶数,一直向右移动
//遇到偶数,则不动。然后右边指针开始向左移动,若没遇到奇数一直向左,
//遇到奇数了,则左右奇偶互换(注:奇偶互换的前提左边<右边,因为防止jjjooo已经换好的情况)
public class Solution {
public int[] reOrderArrayTwo (int[] array) {
int high=array.length-1;
int low=0;
while(low<high){
while(array[low]%2==1) low++;
while(array[high]%2==0) high--;
if(low<high){
int a=array[low];
array[low]=array[high];
array[high]=a; }
}
return array;
}
}
4.字符流中第一个不重复的字符
请实现一个函数用来找出字符流中第一个只出现一次的字符。
string caseout = "";
1.读入测试用例字符串casein
2.如果对应语言有Init()函数的话,执行Init() 函数
3.循环遍历字符串里的每一个字符ch {
Insert(ch);
caseout += FirstAppearingOnce()
}
2. 输出caseout,进行比较。
如果当前字符流没有存在出现一次的字符,返回#字符。
输入"google" 返回值:"ggg#ll"
string caseout = "";
1.读入测试用例字符串casein
2.如果对应语言有Init()函数的话,执行Init() 函数
3.循环遍历字符串里的每一个字符ch {
Insert(ch);
caseout += FirstAppearingOnce()
}
2. 输出caseout,进行比较
public class Solution {
List<Character> list = new ArrayList<>();//储存传过来的字符流
Map<Character,Integer> map = new LinkedHashMap<>(16);//用哈希判断是否重复
public void Insert(char ch)
{
/* Insert 函数是用来接收每次的从字符流传递过来的一个字符举例:
第一次:Insert 传递过来的一个字符: g
就要在 FirstAppearingOnce 函数中去判断当前第一个只出现一次的字符(g) 显然是 g
第二次:Insert 传递过来一个字符: o
就要在 FirstAppearingOnce 函数中去判断当前第一个只出现一次的字符(go) 显然是还是 g
*/
if(!map.containsKey(ch)) map.put(ch,1);
else map.put(ch,map.get(ch)+1);
list.add(ch);//这是统计输入字符流
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
char emp='#';
for(char s:list){
if(map.get(s)==1) {emp=s;break;}//这是找第一个次数char
}
return emp;
}
}
5.孩子们的游戏(圆圈中最后剩下的数)
1)公式
思路:设f(n,m)为n个数取m的结果
0 1 2 3 4 5 。。。。m-1 m m+1 m+2 ......n-1
f(n,m)=0 1 2 3 4 5 ....m-2 m m+1 m+2 ...n-1
重新编号求f(n-1,m)
n-m n-m+1 n-3 n-2 0 1 2 ......n-1-m n-m-1【m-1肯定在其中】
从f(n-1,m)倒推求f(n) 我们可知f(n)=f(n-1,m)+m%n(注意这是n-1倒推n,所以i从2开始)
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (n <1 || m <1) return -1;
int last=0;
for(int i=2;i<=n;i++){
last=(last+m)%i;//注意这里是i不是n!!!!!!
}
return last;
}
2)队列
思路:把全部元素放入队列,在size!=1的时候,不断计数:不是m-1,从队列头取出元素放队尾;是则弹出
/*
add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞
*/
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (n <= 1 || m == 0) return -1;
Queue<Integer> queue=new LinkedList<>();
for(int i=0;i<n;i++){ queue.add(i);}
while(queue.size()!=1){
for(int i=0;i<m-1;i++){
queue.add(queue.remove());//将前面的取出来加在末尾
}
queue.remove();
}
return queue.remove(); }}
3)环形链表模拟圆圈
import java.util.*;
class ListNode {
int val;
ListNode next = null;
ListNode(int val) {this.val = val;}
}
//题目要求从0编号从1计数,再加上链表++k
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (n <= 0 || m<= 0) return -1;
ListNode head=new ListNode(0);
ListNode node=head;
for(int i=1;i<n;i++){
node.next=new ListNode(i);
node=node.next;}
node.next=head;//这时候node的下一个点是头结点,从头结点开始报1.。
int k=0;
while(node.next!=node){//一个点首尾不互联
if(++k==m){//这时候k=m-1
node.next=node.next.next;
k=0; }
else node=node.next;
}
return node.val;
}
}
6. 左旋转字符串
对于一个给定的字符序列 S ,请你把其循环左移 K 位后的序列输出。例如,字符序列 S = ”abcXYZdef” , 要求输出循环左移 3 位后的结果,即 “XYZdefabc” 。
思路:没懂它的意思,应该是S+S+S...从k开始数S字符串长吧,所以n>S.size的时候,n=n%S.size,这样处理。当n=S.size,返回原字符串
也可:原理:YX = (XTYT)T
//1.误差判断
//2.对n进行处理
//3.取0..n的字符串,n+1..的字符串,翻转拼接
public String LeftRotateString(String str,int n) {
char[] ch=str.toCharArray();
if (str == null || str.length() == 0 || n <= 0)
return str;
if (n >= str.length())//例外就是可能n会大于字符串的长度,因此还需要处理
n = n % str.length();
String s1="";String s2="";
for(int i=0;i<n;i++){
s1=s1+ch[i];
}
for(int i=n;i<ch.length;i++){
s2=s2+ch[i];
}
return s2+s1;
}
7.和为S的两个数字
//注意数组为空
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
int low=0;
int high=array.length-1;
ArrayList<Integer> array1=new ArrayList<Integer>();
if(array.length==0) return array1;
while(low<high){
if(array[low]+array[high]>sum) high--;
else if(array[low]+array[high]<sum) low++;
else{
array1.add(array[low]);
array1.add(array[high]);
break;}
}
return array1; }}
8.和为S的连续正数序列
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序(挺简单的,从1开始找)
import java.util.ArrayList;
//双指针法:(某种意义接近滑动窗口)两个起点1和2
//利用等差公式求和SUM,若和等目标,则添加进数组,右指针移动
// 若和<目标,则右指针右移
//... 若和>目标,则左指针右移
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer> > result = new ArrayList<>();
int plow = 1,phigh = 2;
while(plow<phigh){
int cur=(phigh + plow) * (phigh - plow + 1) / 2;
if(cur==sum){
ArrayList<Integer> list= new ArrayList<>();
for(int i=plow;i<=phigh;i++){
list.add(i); }
result.add(list);
phigh++; }
if(cur<sum) phigh++;
if(cur>sum) plow++;
}
return result;
}
}
9.丑数
思路:准备一个数组,存放丑数结果。
每放进一个数就分别乘2,3,5,得到的结果比较大小,然后再放进去
怎么储存三个数组?
1 2 3 4 5 6 8
2 4 6 8 10
3 6 9..12 15
5 10 15 20 25
【这里也可看做三个数组是数组中元素分别乘2,3,5得来的,每个数字分别乘235可以看做独立的,当进去元素那么指针后移】
//1进去后(此时3个指针均在1处),1分别乘2,3,5取最小值=2,p2指针指向2,
//其他均指向1,这时候是2*2,1*3,1*5比较大小
public int GetUglyNumber_Solution(int index) {
if(index<7) return index;
int[] res = new int[index];//存放结果的数组
int a2=0; int a3=0;int a5=0;
res[0]=1;
for(int i=1;i<index;i++){
res[i]=Math.min(res[a2]*2,Math.min(res[a3]*3,res[a5]*5));
if(res[i]==res[a2]*2) a2++;
if(res[i]==res[a3]*3) a3++;
if(res[i]==res[a5]*5) a5++;
}
return res[index-1];
}
9. 数组中出现次数超过一半的数字
1)哈希
keySet(),containKey,
import java.util.*;
public class Solution {
//哈希
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length==1) return array[0];
HashMap<Integer,Integer> map=new HashMap<Integer,Integer>();
for(int i=0;i<array.length;i++){
if(!map.containsKey(array[i])) map.put(array[i],1);
else{
int temp=map.get(array[i]);
temp=temp+1;
map.put(array[i],temp); }
}
for(int resKey : map.keySet()){
if(map.get(resKey)*2>array.length) return resKey;
}
return -1;
}
}
2)排序法
3)投票法
思路:数组中存在众数,那么众数一定大于数组的长度的一半。如果两个数不相等,就消去这两个数。最坏情况下每次消去众数和非众数,那么最终留下的一定是众数
具体做法:
1.维护一个候选众数candidate和它出现的次数count。初始时candidate为首元素,count为 0;
2..我们遍历数组array所有元素,对于每个元素 x,进行如下判断:
1)x==candidate,则count++;
2) x!=candidate时分为两种情况,count>0时,count--;
count=0时,令candidate=x,count=1
public int MoreThanHalfNum_Solution(int [] array) {
if (array==null||array.length==0) return -1;
int candidate = array[0], cnt = 1;
for(int i = 1; i < array.length; i++){
if(candidate==array[i]) cnt++;
else if(cnt>0) cnt--;
else {
candidate = array[i];
cnt = 1; }}
return candidate;}
}
10.调整数组顺序使奇数位于偶数前面(一)
所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
1)暴力解法
import java.util.*;
/*解法粗暴,
遍历一遍数组,然后把奇数放在一个链表里,偶数放在一个链表里,
然后addAll把偶数连在奇数链表的后面,再遍历一遍总链表把值存在新建的数组里面就可以返回了。
*/
public class Solution {
public int[] reOrderArray (int[] array){
if(array==null||array.length<=0) return array;
ArrayList<Integer> odd=new ArrayList<Integer>();
ArrayList<Integer> eve=new ArrayList<Integer>();
for(int i=0;i<array.length;i++){
if(array[i]%2==1) odd.add(array[i]);
else eve.add(array[i]);
}
odd.addAll(eve);
int[] result = new int[odd.size()];
for(int j = 0;j < result.length;j++){
result[j] = odd.get(j);
}
return result;}
}
2) 双指针(O(N),O(N)创立并返回辅助数组)
左指针从头开始扫,遇到奇数,则加上新数组(遇到偶数则不管)
右指针从尾开始扫,遇到偶数,则从尾加入数组
一个while
(left<len && right>=
0
)即可
3)
尾插法,双指针(时间复杂度O(n^2),空间负载度O(1))
思路:一个指针i=0,又一个指针寻找奇数,当奇数下标为j时,则i+1-j-1后移,j插入i的位置,i指针移动(从思路看j=0,j=1是特例,但是编码时可以避开)
public class Solution {
public int[] reOrderArray (int[] array){
int i = 0;
for(int j=0;j<array.length;j++){
if(array[j]%2==1){
int temp=array[j];
for(int k=j;j>i;j--){ array[j]=array[j-1];}
array[i]=temp;
i++;}
}
return array;
}
}
11.替换空格
请实现一个函数,将一个字符串s中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
1) 遍历法(我们需要对长度为n的字符串遍历一次,时间复杂度为O(n),需要存储长度为n的字符串,空间复杂度为O(n))
public String replaceSpace (String s) {
char[] str=s.toCharArray();
String s1="";
for(int i=0;i<str.length;i++){
if(str[i]==' ') s1=s1+"%20";
else s1=s1+str[i];
}
return s1;
}
2)从头到尾扫描字符串,每一次碰到空格的时候做替换。由于是把一个字符替换成3个字符,我们必须要把后面的所有字符都后移两个字节,否则就有两个字符被覆盖了。故假设字符串的长度是n,对每个空格字符,需要移动后面O(n)个字符,因此对含有O(n)个空格的字符串而言总的时间效率是O(n^2)。
3)双指针法
import java.util.*;
/*
对空格进行计数,扩充原数组长度,新长度=原+空格*2,再从后往前替换
*/
public class Solution {
public String replaceSpace (String s) {
if (s == null || s.length() == 0) return "";
int spaceNum = 0;
int m = s.length();
for (int i = 0; i < m; i++) {
char c = s.charAt(i);
if (c == ' ') spaceNum++;}
//p1指向原字符串末尾
int p1 = m - 1;
//p2指向替换之后字符串的末尾,spaceNum为空格数,3是"%20"的长度
int p2 = p1 + spaceNum * 2;
char[] tmp = new char[p2+1];
for (int i = 0; i < s.length(); i++) { tmp[i] = s.charAt(i);}
//当p1和p2指向同一位置时,说明已经替换完毕
while (p1 >= 0 && p1 != p2) {
if (tmp[p1] == ' ') {
tmp[p2--] = '0';
tmp[p2--] = '2';
tmp[p2--] = '%';
}else {
tmp[p2--] = tmp[p1];
}
p1--;
}
return new String(tmp);
}
}
12.第一个只出现一次的字符
在一个长为 字符串中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1.(从0开始计数)
数据范围:0 \le n \le 100000≤n≤10000,且字符串只有字母组成。
要求:空间复杂度 O(n)时间复杂度 O(n)
1)哈希映射
注意:哈希映射的key是无序的,所以我们引入LinkedHashMap
//应该还可以再精简
public int FirstNotRepeatingChar(String str) {
LinkedHashMap<Character,Integer> map=new LinkedHashMap<Character,Integer>();
char[] ch=str.toCharArray();
for(int i=0;i<ch.length;i++){
if(!map.containsKey(ch[i])) map.put(ch[i],1);
else{
int temp=map.get(ch[i]);
temp=temp+1;
map.put(ch[i],temp);
}
}
int k=0;
for(int i=0;i<ch.length;i++){//这里容易错误
if(map.get(ch[i])==1) return k;
else k++;
}
return -1; }
import java.util.*;
public class Solution {
public int FirstNotRepeatingChar(String str) {
LinkedHashMap<Character,Integer> map=new LinkedHashMap<Character,Integer>();
for(int i=0;i<str.length();i++){
if(!map.containsKey(str.charAt(i))) map.put(str.charAt(i),1);
else{
int temp=map.get(str.charAt(i));
temp=temp+1;
map.put(str.charAt(i),temp);
}
}
int k=0;
for(int i=0;i<str.length();i++){
if(map.get(str.charAt(i))==1) return k;
else k++;
}
return -1;
}
}