1.给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。说明:算法应该具有线性复杂度,不可以使用额外的空间来实现。
解:初看这道题,想到的是把数组中的每一个元素和其他所有元素进行比较,但是这样时间复杂度不满足线性,之后读题理解到,重复的数字均出现2次,这样就可以用异或来解这道题,因为一个数字和它本身异或为0,和0异或是它本身。
class Solution {
public int singleNumber(int[] nums) {
int num = nums[0];
for(int i = 1 ;i < nums.length; i++){
num = nums[i] ^ num;
}
return num;
}
}
2.给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
解:刚开始想的是把数组中的数字合成一个整数,然后加一,在把整数拆分放到数组里面,然而这个方法只能解决一部分题目,遇到特别长的数组,int和long这样的数据类型都无法存储。苦思无解,看了别人的解法,十分巧妙,记下来。
class Solution {
public int[] plusOne(int[] digits) {
for (int i = digits.length - 1; i >= 0; i--) {
digits[i]++;
digits[i] = digits[i] % 10;
if (digits[i] != 0) return digits;//没有遇到9,99,999这样的情况,就会从这里退出
}
digits = new int[digits.length + 1];//遇到9,99这样的情况,手动给它进位
digits[0] = 1;
return digits;
}
}
3.给定一个包含 0, 1, 2, ..., n
中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。
解:因为缺少一个数字,可以创建一个新的数组比原数组空间多一个,然后把原数组中的值当作新数组的角标,将新数组赋值为1,这样新数组中会有一个元素为0,其角标就为缺少的值。(线性复杂度,线性空间)
class Solution {
public int missingNumber(int[] nums) {
int[] newArr = new int[nums.length+1];
for(int i = 0 ; i < nums.length ; i++){
newArr[nums[i]] = 1;
}
int j = 0;
for( j = 0 ; j < newArr.length ; j++){
if(newArr[j] == 0){
break;
}
}
return j;
}
}
但是,还有更好解法,根据题目的特殊性,我们可以用前n项公式和求和,接着逐一减去数组中的值。
class Solution {
public int missingNumber(int[] nums) {
int sum = (1 + nums.length ) * nums.length / 2;
for(int i = 0 ; i < nums.length;i++ ){
sum -= nums[i];
}
return sum;
}
}
4.给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋
的元素。
解:我选择的是先进行排序,然后用遍历一遍,利用计数器来找到众数。
class Solution {
public int majorityElement(int[] nums) {
for(int i = 0 ;i < nums.length - 1 ; i++) {
for (int j = 0; j < nums.length - 1 - i; j++) {
if(nums[j] > nums[j+1]) {
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}//冒泡排序
int count = 1;
int num =nums[0];
for(int i = 1 ; i < nums.length ; i++){
if(nums[i] == num){
count++;
if(count > nums.length /2)
break;
}else{
num = nums[i];
count = 1;
}
}
return num;
}
}
但是测试用例44个只通过了42个,他给了一个很大的数组,导致解题时间超出限制。
第二种思想:既然众数是出现次数1/2频度以上,那么若现将数组排序,中位数必为众数!(十分巧妙!)
class Solution {
public int majorityElement(int[] nums) {
int length = nums.length;
Arrays.sort(nums);
return nums[length/2];
}
}
第三种思想:摩尔投票法:其他数字出现次数的总和都是比不上这个数字出现的次数 。
设置两个变量 candidate 和 count,candidate 用来保存数组中遍历到的某个数字,count 表示当前数字的出现次数,一开始 candidate 保存为数组中的第一个数字,count 为 1
遍历整个数组
如果数字与之前 candidate 保存的数字相同,则 count 加 1
如果数字与之前 candidate 保存的数字不同,则 count 减 1
如果出现次数 count 变为 0 ,candidate 进行变化,保存为当前遍历的那个数字,并且同时把 count 重置为 1
遍历完数组中的所有数字即可得到结果
class Solution {
public int majorityElement(int[] nums) {
int num = nums[0];
int count = 1;
for(int i = 0 ; i < nums.length ; i++ ){
if(nums[i] == num)
count++;
else{
count--;
if(count==0){
num = nums[i];
count = 1;
}
}
}
return num;
}
}
5.在一个给定的数组nums
中,总是存在一个最大元素 。查找数组中的最大元素是否至少是数组中每个其他数字的两倍。如果是,则返回最大元素的索引,否则返回-1。
解:一次遍历,找到最大数字和第二大数字,进行比较即可。
class Solution {
public int dominantIndex(int[] nums) {
if(nums.length == 1)
return 0;
int max = -1;
int secondMax = -1;
int index = -1;
for(int i = 0 ;i < nums.length ;i++){
if(nums[i] > max){
secondMax = max;
max = nums[i];
index = i;
}else{
if(nums[i] >secondMax)
secondMax = nums[i];
}
}
if(max / 2 >= secondMax)
return index;
else
return -1;
}
}
6.给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:初始化 nums1 和 nums2 的元素数量分别为 m 和 n。你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
解: 这道题可以用双指针来解决,类比两堆扑克牌,我把他们倒扣在桌面上,每次打开两牌堆顶上各一张,谁小,就把它放在第三堆,依次进行。这道题由于nums1有足够的空间,双指针可以从后面开始。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1 = m - 1;
int p2 = n - 1;
int p = m + n -1;
while((p1>=0) && (p2 >=0)){
nums1[p--] = (nums1[p1] < nums2[p2]) ? nums2[p2--] : nums1[p1--];
}
System.arraycopy(nums2, 0, nums1, 0, p2 + 1); //如果第一个数组提前结束,就把第二个数组剩下的元素放入第一个
}
}
7.(两数之和)给定一个整数数组 nums
和一个目标值 target
,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
解:最简单的办法就是暴力解法,用两个for循环来解决问题。
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i = 0 ; i < nums.length ;i++){
for(int j = i + 1 ; j < nums.length ; j++){
if(nums[j] == target - nums[i])
return new int[] { i, j };
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
还有一种方法是利用哈希表来解决问题。
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();//创建一个哈希表
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];//要查询的元素
if (map.containsKey(complement)) { //如果要查询的元素在哈希表里就返回位置
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);//没有找到,就将它和它的角标加入哈希表
}
throw new IllegalArgumentException("No two sum solution");
}
}
8.(三数之和)
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
解:1.首先对数组进行排序,排序后固定一个数nums[i]nums[i]nums[i],再使用左右指针指向nums[i]nums[i]nums[i]后面的两端,数字分别为nums[L]nums[L]nums[L]和nums[R]nums[R]nums[R],计算三个数的和sumsumsum判断是否满足为 000,满足则添加进结果集
2.如果nums[i]nums[i]nums[i]大于 000,则三数之和必然无法等于 000,结束循环
3.如果nums[i]nums[i]nums[i] == nums[i−1]nums[i-1]nums[i−1],则说明该数字重复,会导致结果重复,所以应该跳过
4.当sumsumsum == 000 时,nums[L]nums[L]nums[L] == nums[L+1]nums[L+1]nums[L+1]则会导致结果重复,应该跳过,L++L++L++
5.当sumsumsum == 000 时,nums[R]nums[R]nums[R] == nums[R−1]nums[R-1]nums[R−1]则会导致结果重复,应该跳过,R−−R--R−−
时间复杂度:O(n2)O(n^2)O(n2),nnn为数组长度
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList();
Arrays.sort(nums); //排序
int len = nums.length;
if(nums == null || len < 3) return ans;
for(int i = 0 ; i < len ; i++){
if(nums[i] > 0 ) break;
if(i > 0 && nums[i] == nums[i-1]) continue;
int L = i+1;
int R = len -1;
while(L < R){
int sum = nums[i] + nums[L] +nums[R];
if(sum == 0){
ans.add(Arrays.asList(nums[i] , nums[L] , nums[R]));
while (L<R && nums[L] == nums[L+1]) L++; // 去重
while (L<R && nums[R] == nums[R-1]) R--; // 去重
L++;
R--;
}else if(sum < 0){
L++;
}else{
R--;
}
}
}
return ans;
}
}