1. 只出现一次的数字
leetcode题目链接:136. 只出现一次的数字
给你一个 非空 整数数组
nums,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
**输入:**nums = [2,2,1]
**输出:**1
示例 2 :
**输入:**nums = [4,1,2,1,2]
**输出:**4
示例 3 :
**输入:**nums = [1]
**输出:**1
解法:
java
class Solution {
public int singleNumber1(int[] nums) {
Set<Integer> set = new HashSet<>();
// 对于每一个num,第一次遍历到的时候加入set,第二次再遇到就从set中删除,最后剩下的就是只出现一次的元素
for(int num : nums){
if(set.contains(num)){
set.remove(num);
}else{
set.add(num);
}
}
// 获取set中的唯一元素
if(set.size() == 1){
return set.iterator().next();
}
return -1;
}
/**
* 找出数组中只出现一次的元素
* 使用异或运算的性质:相同数异或为0,任何数与0异或为自身
* @param nums 非空整数数组,其中除一个元素外,其余均出现两次
* @return 只出现一次的元素
*
* 示例1:输入 [2,2,1],计算过程为 2 ^ 2 ^ 1 = 0 ^ 1 = 1。
* 示例2:输入 [4,1,2,1,2],计算过程为 4 ^ 1 ^ 2 ^ 1 ^ 2 = 4 ^ (1^1) ^ (2^2) = 4 ^ 0 ^ 0 = 4。
*/
public int singleNumber(int[] nums) {
int result = 0; // 初始结果为0
for (int num : nums) {
result ^= num; // 遍历数组,将所有元素进行异或运算
}
return result; // 最终结果即为只出现一次的元素
}
}
2. 多数元素
leetcode题目链接:169. 多数元素
给定一个大小为
n的数组nums,返回其中的多数元素。多数元素是指在数组中出现次数 大于⌊ n/2 ⌋的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3]
输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
解法:
java
class Solution {
public int majorityElement1(int[] nums) {
int n = nums.length;
Map<Integer, Integer> map = new HashMap();
// 统计每一个num出现的次数,并返回出现次数大于n/2的元素
for(int num : nums){
map.put(num, map.getOrDefault(num, 0)+1);
if(map.get(num) > n/2){
return num;
}
}
return -1;
}
public int majorityElement(int[] nums) {
int candidate = nums[0]; // 初始化候选元素为第一个元素
int count = 1; // 计数器初始化为1
for (int i = 1; i < nums.length; i++) { // 从第二个元素开始遍历
if (count == 0) { // 如果计数器为0,更新候选元素为当前元素
candidate = nums[i];
count = 1;
} else if (nums[i] == candidate) { // 当前元素与候选元素相同,计数器加1
count++;
} else { // 当前元素与候选元素不同,计数器减1
count--;
}
}
return candidate; // 遍历结束后,候选元素即为多数元素
}
}
3. 颜色分类
leetcode题目链接:75. 颜色分类
给定一个包含红色、白色和蓝色、共
n个元素的数组nums,**原地**对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。我们使用整数
0、1和2分别表示红色、白色和蓝色。必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
解法:
java
class Solution {
public void sortColors(int[] nums) {
int n = nums.length;
int left = 0;
// 双指针,固定left指针,用right指针按照顺序先后去找0、1、2,找到对应的值就放在left指针位置上,找完0,找1,最后找2
for(int i = 0; i <= 2; i++){
int right = left;
while(right < n){
if(nums[right] == i){
// 交换left和right的元素,left向前走一步
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++;
}
right++;
}
}
}
}
4. 下一个排列
leetcode题目链接:31. 下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
arr = [1,2,3],以下这些都可以视作arr的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1]。整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,
arr = [1,2,3]的下一个排列是[1,3,2]。- 类似地,
arr = [2,3,1]的下一个排列是[3,1,2]。- 而
arr = [3,2,1]的下一个排列是[1,2,3],因为[3,2,1]不存在一个字典序更大的排列。给你一个整数数组
nums,找出nums的下一个排列。必须**原地**修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]
解法:
java
class Solution {
/**
* 1. 寻找转折点:从数组末尾开始向前查找第一个满足nums[i] < nums[i+1]的位置i。这个位置是下一个排列需要调整的起点。
* 2. 交换元素:如果找到了这样的i,再从末尾开始找到第一个比nums[i]大的元素nums[j],并交换它们的位置。这一步确保了后续的排列是更大的。
* 3. 反转后续部分:将i之后的元素反转,使其变为升序排列,这样可以得到最小的下一个排列。如果整个数组已经是降序排列(即i未找到),则直接反转整个数组得到最小排列。
*/
public void nextPermutation(int[] nums) {
// 从后向前找到第一个相邻的升序对
int i = nums.length - 2;
while (i >= 0 && nums[i] >= nums[i + 1]) {
i--;
}
// 如果找到这样的i,则找到右边比nums[i]大的最小元素进行交换
if (i >= 0) {
int j = nums.length - 1;
while (j >= 0 && nums[j] <= nums[i]) {
j--;
}
swap(nums, i, j);
}
// 反转i之后的元素,使其升序排列,得到最小的下一个排列
reverse(nums, i + 1);
}
private void reverse(int[] nums, int start) {
int left = start, right = nums.length - 1;
while (left < right) {
swap(nums, left, right);
left++;
right--;
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
5. 寻找重复数
leetcode题目链接:287. 寻找重复数
给定一个包含
n + 1个整数的数组nums,其数字都在[1, n]范围内(包括1和n),可知至少存在一个重复的整数。假设
nums只有 一个重复的整数 ,返回 这个重复的数 。你设计的解决方案必须 不修改 数组
nums且只用常量级O(1)的额外空间。
示例 1:
输入:nums = [1,3,4,2,2]
输出:2
示例 2:
输入:nums = [3,1,3,4,2]
输出:3
示例 3 :
输入:nums = [3,3,3,3,3]
输出:3
解法:
java
class Solution {
/**
* 1. 初始化指针:left 初始化为 1,right 初始化为数组长度减 1(即 n)。
* 2. 二分查找循环:当 left 小于 right 时,计算中间值 mid。
* 3. 统计数量:遍历数组,统计小于等于 mid 的元素数量 count。
* 4. 调整区间:根据 count 和 mid 的比较结果调整左右指针,缩小搜索范围。
* 5. 返回结果:当 left 和 right 相遇时,left 即为重复数。
*/
public int findDuplicate(int[] nums) {
int left = 1;
int right = nums.length - 1; // n = nums.length - 1
while (left < right) {
int mid = left + (right - left) / 2;
int count = 0;
for (int num : nums) {
if (num <= mid) {
count++;
}
}
// 根据鸽巢原理,如果count > mid,说明重复数在左半部分
if (count > mid) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
/**
* 使用索引来标记已经出现过的数字
* 但是这个解法改变了原是数组中的值,不满足题目要求
*/
public int findDuplicate2(int[] nums) {
int n = nums.length;
int result = nums[0];
// 把result即数组中对应的值,找到nums中索引为result的地方设置为0
while(result != 0){
// 如果result索引对应的值已经是0,说明result已经出现过了,原始数组中的值没有0
if(nums[result] == 0){
return result;
}
// 索引result的值设置为0
int temp = nums[result];
nums[result] = 0;
result = temp;
}
return result;
}
}