(1)用算法高效寻找素数
class Solution {
public int countPrimes(int n) {
boolean[] isPrim=new boolean[n];
Arrays.fill(isPrim,true);
//优化1:只需要判断到sqrt(n)就能知道是否为质数
for(int i=2;i*i<n;i++){
if(isPrim[i]){
//优化2:从i*i开始遍历,避免重复遍历
for(int j=i*i;j<n;j+=i){
isPrim[j]=false;
}
}
}
int count=0;
for(int i=2;i<n;i++){
if(isPrim[i]){
count++;
}
}
return count;
}
}
(2)高效进行模幂运算
class Solution {
int base=1337;
public int superPow(int a, int[] b) {
LinkedList<Integer> list=new LinkedList<>();
for(int i:b){
list.add(i);
}
return helper(a,list);
}
public int helper(int a,LinkedList<Integer> list){
if(list.size()==0) return 1;
int last=list.removeLast();
int part1=mypow(a,last);
int part2=mypow(helper(a,list),10);
return (part1*part2)%base;
}
public int mypow(int a,int k){
if(k==0) return 1;
a=a%base;
if(k%2==1){
return( a*mypow(a,k-1))%base;
}else{
int p=mypow(a,k/2);
return (p*p)%base;
}
}
}
幂运算mypow采用快速幂运算:
取模是根据:
(ab)%k=((a%k)(b%k))%k
总的幂运算helper是采用递归:
(3)寻找缺失元素
class Solution {
public int missingNumber(int[] nums) {
int n=nums.length;
//1.普通遍历方法
// if(nums[0]!=0) return 0;
// if(nums[n-1]!=n) return n;
// int res=-1;
// for(int i=0;i<n-1;i++){
// if(nums[i+1]-nums[i]>1){
// res=i+1;
// }
// }
// return res;
//2.等差数列求和
int sum_expect=(0+n)*(n+1)/2;
int sum=0;
for(int i:nums){
sum+=i;
}
return sum_expect-sum;
}
}
(4)高效寻找缺失和重复的数字
1.暴力方法求解:
class Solution {
public int[] findErrorNums(int[] nums) {
int n=nums.length;
int dup=-1,missing=-1;
for(int i=1;i<=nums.length;i++){
int count=0;
for(int j=0;j<nums.length;j++){
if(nums[j]==i){
count++;
}
}
if(count==2){
dup=i;
}
if(count==0){
missing=i;
}
//优化:若找到,提前结束,不用遍历全部
if (dup > 0 && missing > 0)
break;
}
return new int[]{dup,missing};
}
}
2.利用排序
class Solution {
public int[] findErrorNums(int[] nums) {
int dup=-1,missing=1;
Arrays.sort(nums);
for(int i=1;i<nums.length;i++){
if(nums[i]-nums[i-1]==0){
dup=nums[i];
}
if(nums[i]-nums[i-1]>1){
missing=nums[i-1]+1;
}
}
return new int[]{dup,nums[nums.length - 1] != nums.length ? nums.length : missing};
}
}
//时间复杂度:O(n\log n)O(nlogn),排序需要 O(n\log n)O(nlogn) 的时间。
3.使用map
class Solution {
public int[] findErrorNums(int[] nums) {
int dup=-1,missing=1;
Map<Integer,Integer> mapping=new HashMap<>();
for(int i:nums){
mapping.put(i,mapping.getOrDefault(i,0)+1);
}
for(int i=1;i<=nums.length;i++){
if(mapping.containsKey(i)){
if(mapping.get(i)>1){
dup=i;
}
}else{
missing=i;
}
}
return new int[]{dup,missing};
}
}
4.labuladong巧解:将访问过的位置变为负数
class Solution {
public int[] findErrorNums(int[] nums) {
int dup=-1;
//思路:将数组nums中访问过的位置变为负数,那么当访问到某个值为负数时,这个就是重复元素
for(int i=0;i<nums.length;i++){
int index=Math.abs(nums[i])-1;
if(nums[index]<0){
dup=Math.abs(nums[i]);
}else{
nums[index]*=-1;
}
}
//大于0的位置则为缺失元素
int missing=-1;
for(int i=0;i<nums.length;i++){
if(nums[i]>0){
missing=i+1;
}
}
return new int[]{dup,missing};
}
}
(5)巧用异或
class Solution {
public int[] singleNumbers(int[] nums) {
if(nums.length < 2)
return nums;
//总体思路:使用分组异或
int[] result=new int[2];
//1.先求出所有元素异或结果,此时所得结果即为两个出现一次数字的异或结果
int ret=nums[0];
for(int i=1;i<nums.length;i++){
ret^=nums[i];
}
//2.找到异或结果中最右边不为0的位置
int bitIndex=0;
for(int i=0;i<32;i++){
if((ret>>i&1)==1){
bitIndex=i;
break;
}
}
//3.根据找到的这个位置进行分组,分组之后异或的结果就是两个只出现一次的数字
for(int j:nums){
if((j>>bitIndex&1)==1){
result[0]^=j;
}else{
result[1]^=j;
}
}
return result;
}
}
(6)在无限序列中随机抽取元素
A.取链表中随机一个节点的值
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
ListNode head;
/** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
public Solution(ListNode head) {
this.head=head;//定义构造函数
}
/** Returns a random node's value. */
public int getRandom() {
Random r=new Random();
int i=0,res=-1;
ListNode p=head;
while(p!=null){
//这样的话概率为1/i
if(r.nextInt(++i)==0){
res=p.val;
}
p=p.next;
}
return res;
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(head);
* int param_1 = obj.getRandom();
*/
B.随机取一个数在数组中的索引
class Solution {
int[] nums;
public Solution(int[] nums) {
this.nums=nums;
}
public int pick(int target) {
Random r=new Random();
//1.暴力法,如果数组很大则需要占用很多空间
// List<Integer> index=new LinkedList();
// for(int i=0;i<nums.length;i++){
// if(nums[i]==target){
// index.add(i);
// }
// }
// int sz=index.size();
// return index.get(r.nextInt(sz));
//2.蓄水池抽样
int count=0,res=0;
for(int i=0;i<nums.length;i++){
//以1/count的概率进行抽样,无所谓总共有多少target,截止到当前这个target要保证抽样概率为1/count
if(nums[i]==target){
if(r.nextInt(++count)==0){
res=i;
}
}
}
return res;
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(nums);
* int param_1 = obj.pick(target);
*/
(7)高效对有序数组/链表去重
A.数组
class Solution {
public int removeDuplicates(int[] nums) {
//使用快慢指针方法,最后nums[0~slow]的元素无重复
if(nums.length==0) return 0;
int slow=0,fast=1;
while(fast<nums.length){
if(nums[fast]!=nums[slow]){
slow++;
nums[slow]=nums[fast];
}
fast++;
}
return slow+1;
}
}
B.链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head==null) return null;
ListNode slow=head,fast=head.next;
while(fast!=null){
if(fast.val!=slow.val){
slow.next=fast;
slow=slow.next;
}
fast=fast.next;
}
slow.next=null;
return head;
}
}
(8)智力题
A.
class Solution {
public boolean canWinNim(int n) {
// 如果上来就踩到 4 的倍数,那就认输吧
// 否则,可以把对方控制在 4 的倍数,必胜
return n%4!=0;
}
}
B.
class Solution {
public boolean stoneGame(int[] piles) {
//由于只能在左右两端拿,所以可以在开始时候选奇数堆(1)还是偶数堆(piles.length),
//通过观察看奇数堆总石子数多还是偶数堆多,就选哪个
//所以可以总是赢
return true;
}
}
C.
class Solution {
public int bulbSwitch(int n) {
//平方数被按了奇数次,所以最后亮着
//例如:16=1*16=2*8=4*4,所以第16盏灯共被按了5次(1,2,4,8,16)
return (int)Math.sqrt(n);
}
}