文章目录
LeetCode有时候报错很怪,漏掉可能的返回情况也会报错。把return语句写while外面???
day1----二分查找
1.二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
int search(int* nums, int numsSize, int target) {
int low = 0;
int res = 0;
int high = numsSize - 1;//
int mid = (low + high) / 2;//
while (low <= high) {
mid = (low + high) / 2;//2 4
if (nums[mid] > target) {
high = mid - 1;
}
else if (nums[mid] < target) {
low = mid + 1;//3
}
else {
return mid;
//res = mid;
// break;
}
}
return -1;
}
2.第一个错误的版本
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例 1:
输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
示例 2:
输入:n = 1, bad = 1
输出:1
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
int firstBadVersion(int n) {
int left = 1;
int right = n;
// if (isBadVersion(1)) {
// return 1;
// }
while (left <= right) {
int mid =left+ (right-left) / 2;//防止溢出
if (isBadVersion(mid)) {//出错
right = mid-1;//向左边找
}
else if (!isBadVersion(mid)) {
left = mid + 1;
}
}
return left;
}
3.搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5 输出: 2 示例 2:
输入: nums = [1,3,5,6], target = 2 输出: 1 示例 3:
输入: nums = [1,3,5,6], target = 7 输出: 4
int searchInsert(int* nums, int numsSize, int target){
int low = 0;
int res = 0;
int high = numsSize - 1;//5
int mid = (low + high) / 2;//1
while (low <= high) {
mid = (low + high) / 2;//1
if (nums[mid] > target) {
high = mid - 1;
}
else if (nums[mid] < target) {
low = mid + 1;//3
}
else {
//res = mid;
return mid;
}
}
//比二分查找多了一个判断 需要判断插在哪里
if(nums[mid]>target){//如果nums[mid]>target 那么target的位置就该是mid
return mid;
}else {//如果nums[mid]<=target 则target应该在mid后面
int res1 = mid+1;
return res1;
}
}
day2----双指针
1.平方数组排序
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100] 示例 2:输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]
注意 c语言不可以传引用,malloc函数的使用 malloc(空间大小)
// void swap(int &a,int &b){
// int temp = a;
// a = b;
// b = temp;
// }
int* sortedSquares(int* nums, int numsSize, int* returnSize) {
//使用双指针
//返回的是指针,需要自己设计
*returnSize = numsSize;//returnSize是返回的数组大小
//int* res = (int*)malloc(numsSize);//这样写数组越界
//因为malloc内的申请的空间大小就是数组的大小,numsSize只是表示有n个数,但是一个数占的字节大小是sizeof(int),所以就是 numsSize*sizeof(int)
int* res = malloc(numsSize * sizeof(int));
//也可以写成
//int* res = (int*)malloc(numsSize*4);
int left = 0;
int right = numsSize - 1;
int i = numsSize - 1;//指向新数组
while (left <= right) {//因为从第一个负数和最后一个整数比较的话,如果最后一个数的平方大于第一个,则一定最大
if (nums[left] * nums[left] < nums[right] * nums[right]) {
//swap(nums[left],nums[right]);
res[i--] = nums[right] * nums[right];
right--;//指针向左移动
}
else {
res[i--] = nums[left] * nums[left];
left++;//指针向右移动
}
//i--;
}
return res;
}
2.轮转数组
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
//这不是一个好的办法,空间复杂度比较大
void rotate(int* nums, int numsSize, int k) {
//双指针
int i = 0;
int j = numsSize - 1;
int a[100090];
int pos = 0;
//轮转的位置 (now_pos+k)%numsSize
//先讨论特殊情况
if (k % numsSize == 0) {//轮转的次数是元素个数的整数倍
return;
}
else
k = k % numsSize;
//利用额外数组先把要轮转的存起来
for (i = numsSize - k; i < numsSize ; i++) {
a[pos] = nums[i];
pos++;
}
//剩下的元素向后轮转
for (i = numsSize - 1; i >= k; i--) {
nums[i] = nums[i - k];
}
//两个数组合并
for (i = 0; i < k; i++) {
nums[i] = a[i];
}
}
直接对数组翻转可以使空间复杂度将至O(1)
当数组的元素向右移动 k 次以后,后面 k mod n 个元素会被移到前面。
可以先把整个数组翻转,这样尾部的 k mod n个元素就以倒序排列在前面,再把前面 k mod n 个元素翻转,后面 n-kmodn 个元素翻转即可
//C语言不可以传引用
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
void reserve(int num[], int a, int b) {
int i = a;
int j = b;
for (i = a; i <= j; i++) {
swap(&num[i], &num[j]);
j--;
}
}
void rotate(int* nums, int numsSize, int k) {
k = k % numsSize;
reserve(nums, 0, numsSize - 1);//整个数组翻转
reserve(nums, 0, k - 1);//翻转前半部分
reserve(nums, k, numsSize - 1);//翻转后半部分
}
day3----双指针
1.移动0
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12] 输出: [1,3,12,0,0] 示例 2:
输入: nums = [0] 输出: [0]
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
void moveZeroes(int* nums, int numsSize) {
//如果不要求原地操作的话,可以计算出有多少个零,全部补到后面即可
//双指针
//左指针指向第一个0,右指针指向0后的第一个非零数,二者交换
int left = 0;
int right = 0;
if (numsSize == 1) {
return;
}
while (right < numsSize) {
//对于第一步,如果进不去这个if判断,说明nums[0] = 0,因此left=0时nums[left]=0;
//当nums[right]!=0时,两个数进行交换,left向后移动
//因为只有nums[right]!=0的时候才会进入判断语句,left才会有改变,如果left此时也不为0,right和left是同步变化的
//比如nums[0] = 1;此时进入if语句,当进行交换时,实际上就是nums[0]和nums[0]进行交换,然后left right都+1
if (nums[right] != 0) {//right指向不为0的数
swap(nums + left, nums + right);//交换
left++; //指向0的指针向后移动
}
right++;
}
}
2.两数之和
给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
输入:numbers = [2,7,11,15], target = 9 输出:[1,2] 解释:2 与 7 之和等于目标数 9。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
示例 2:输入:numbers = [2,3,4], target = 6 输出:[1,3] 解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
示例 3:输入:numbers = [-1,0], target = -1 输出:[1,2] 解释:-1 与 0 之和等于目标数 -1 。因此index1 = 1, index2 = 2 。返回 [1, 2] 。
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize) {
int* res = (int*)malloc(sizeof(int) * 2);
*returnSize = 2;//需要给出返回值的大小
int left = 0;
int right = numbersSize - 1;
//这一段只适用于一部分后面数字大于0的,如果后面target = -2,numbers[1] = 0,numbers[0] = -2;
//right就会--,无法进入while循环,结果错误
// while(numbers[right]>target){
// right--;
// }
while (left < right) {
int sum = numbers[left] + numbers[right];
if (sum < target) {
left++;
}
if (sum > target) {
right--;
}
if (sum == target) {
res[0] = left + 1;
res[1] = right + 1;
return res;
}
}
//别的情况
res[0] = -1;
res[1] = -1;
return res;
}
day4----双指针
1.翻转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = [“h”,“e”,“l”,“l”,“o”] 输出:[“o”,“l”,“l”,“e”,“h”] 示例 2:
输入:s = [“H”,“a”,“n”,“n”,“a”,“h”] 输出:[“h”,“a”,“n”,“n”,“a”,“H”]
void reverseString(char* s, int sSize){
//传进来数组起始地址和数组大小
char *left = s;//指向起始地址
char *right = s+sSize-1;//终止字符
//直接修改原字符串的话,实际就是通过中间变量交换两个首尾
while(left<right){
char ch = *left;
*left = *right;
*right = ch;//交换了两个字符,需要继续向中间靠拢
left++;
right--;//没有加*就说明是地址变化
}
}
2.翻转字符串中的单词
给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例 1:
输入:s = “Let’s take LeetCode contest” 输出:“s’teL ekat edoCteeL tsetnoc”
示例 2:输入: s = “God Ding” 输出:“doG gniD”
void swap(char* a, char* b) {
char temp = *a;
*a = *b;
*b = temp;
}
char* reverseWords(char* s) {
char *q = s;
int sum = 0;
int sum_s = strlen(s);
int left = 0;
int right = 0;
//之前用q是否空作为终止条件,但是q又不需要跟着向后移动,使用字符串的长度
while (sum<sum_s) {
left = sum;
//第一步计算一个单词的长度
//终止读取
while (q[sum]!='\0' && q[sum] != ' ') {
sum++;//读取到空格才停
//q++;//不需要改变指针,因为后面调换时p作为基准
}
//翻转第一个单词
right = sum - 1;
while (left < right) {
swap(q + left, q + right);
left++;
right--;
}
//要越过空格读取下一个单词
while (q && q[sum] == ' ') {
//越过空格
sum++;
}
}
return q;
}
day5----双指针
1.链表中间节点
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5] 输出:此列表中的结点 3 (序列化形式:[3,4,5]) 返回的结点值为 3 。
(测评系统对该结点序列化表述是 [3,4,5])。 注意,我们返回了一个 ListNode 类型的对象 ans,这样: ans.val =
3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next =
NULL. 示例 2:输入:[1,2,3,4,5,6] 输出:此列表中的结点 4 (序列化形式:[4,5,6]) 由于该列表有两个中间结点,值分别为 3 和
4,我们返回第二个结点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head){
//双指针 一个走一步,一个走两步,最后节点到达终点的时候,慢的指针在中间
struct ListNode* slow = head;
struct ListNode* fast = head;
//一定要同步进行
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
2.删除链表中的倒数第n个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
示例 2:输入:head = [1], n = 1 输出:[]
示例 3:输入:head = [1,2], n = 1 输出:[1]
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
//加入要删除倒数第n个,就让快指针先走n步
//等快指针指向结尾的时候,慢指针指向要删除节点的前驱节点
struct ListNode* slow = head;
struct ListNode* fast = head;
struct ListNode* q = head;
int num = 0;
// 需要判断输入是否合法
//判断一下删除第一个结点的特殊情况
while (q) {
q = q->next;
num++;
}
//printf("%d\n",num);
if (num == n) {
head = slow->next;
free(slow);
return head;
}
//普通情况,fast先走n步
for (int i = 0; i < n; i++) {
fast = fast->next;
}
//printf("%d",slow->val);
//printf("%d %d",slow->val,fast->val);
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next;
}
//slow指向前驱节点
struct ListNode* p = slow->next;//指向被删除的节点
printf("%d", slow->val);
if (p != NULL) {
slow->next = p->next;
free(p);
}
else {
return NULL;
}
return head;
}
day6----动态规划
1.打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。 示例 2:输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5
号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
int max(int dp[], int n, int m) {
int max_num = dp[0];
for (int i = n; i <= m; i++) {
if (max_num < dp[i]) {
max_num = dp[i];
}
}
return max_num;
}
int rob(int* nums, int numsSize) {
//dp数组含义 dp[]存放的是最大的金额
//从i开始,j从2开始
//因为不能相邻 dp[i] = max(dp[i-j]);
//初始化
int dp[110];
for (int i = 0; i < numsSize; i++) {
dp[i] = nums[i];
}
if (numsSize == 1 || numsSize == 2) {
return max(dp, 0, numsSize - 1);
}
dp[2] = dp[0] + dp[2];
printf("%d %d\n", dp[2], dp[numsSize - 1]);
int max_num = 0;
for (int j = 3; j < numsSize; j++) {
//for(int k = 2;k<j;k++){
//n是下限,m是上限
max_num = max(dp, 0, j - 2);
printf("%d ", max_num);
dp[j] = dp[j] + max_num;
//}
}
if (dp[numsSize - 1] >= dp[numsSize - 2]) {
return dp[numsSize - 1];
}
else {
return dp[numsSize - 2];
}
}
2.三角形最小路径和
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1
的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。示例 1:
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]] 输出:11 解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
示例 2:输入:triangle = [[-10]] 输出:-10
int min(int n, int m) {
if (n < m) {
return n;
}
else {
return m;
}
}
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize) {
// 数组dp[i][j]就是最小的路径和
//dp[i][j] = min(dp[i-1][j],dp[i-1][j-1])+triangle[i]j];
//初始化
//初始化还需要考虑对角线以上的元素,因为那些元素不存在,所以也要特殊初始化
int dp[triangleSize][triangleSize];
dp[0][0] = triangle[0][0];
printf("%d ", dp[0][0]);
int i, j;
for (i = 1; i < triangleSize; i++) {
for (j = 0; j <= i; j++) {
if (j == 0) {
dp[i][0] = dp[i - 1][0] + triangle[i][0];
printf("%d ", dp[i][0]);
}
else if (i == j) {
dp[i][j] = triangle[i][j] + dp[i - 1][j - 1];
}
else {
dp[i][j] = triangle[i][j];
}
}
}
for (i = 1; i < triangleSize; i++) {
for (j = 1; j < i; j++) {//这里不需要写 j<=i 因为对角线上的元素特殊处理
dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j];
}
}
//返回的是哪个?
int min_num = min_num = dp[triangleSize - 1][0];
for (int k = 1; k < triangleSize; k++) {
if (min_num > dp[triangleSize - 1][k]) {
min_num = dp[triangleSize - 1][k];
}
}
return min_num;
}
3.数字拆分
给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。返回 你可以获得的最大乘积 。
示例 1:
输入: n = 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:输入: n = 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
//确定数组及其下标
//dp[i]就表示最大乘积
//递推公式
//可以拆成2个及以上,拆成两个 j*(i-j),拆成多个 dp[i-j]*j
//dp[i] = dp[i-j]
//和化积最大应该找根号n
//例如 根号10约等于3.2,得到3,10%3 = 4,积的形式就是3*4*3
int dp[60] = { 0 };
int max(int a, int b) {
if (a > b) {
return a;
}
else {
return b;
}
}
int integerBreak(int n) {
for (int i = 2; i <= n; i++) {//外层
int max_num = 0;
for (int j = 1; j < i; j++) {
//因为可以拆成很多个,所以每次j++的时候要与上一次的最大值比较一下
//实际就是暴力遍历了一遍
max_num = max(max_num, max(j * (i - j), j * dp[i - j]));
}
//最终得到的最大值
dp[i] = max_num;
}
return dp[n];
}
day7----位运算
1. 2的幂
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false
。如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。
示例 1:
输入:n = 1 输出:true 解释:20 = 1
示例 2:输入:n = 16 输出:true 解释:24 = 16
bool isPowerOfTwo(int n) {
if (n <= 0) {
return false;
}
//别的情况,写成二进制,最后一位必须是0,并且如果是二进制且为2的幂次方的话,只允许有一个最高位是1
//例如 001,010,100,1000,10000. 1010,就不是2的幂次方
//写成二进制,进行与运算
//因为每个数都是aaaa100...,一定有一个1,如果n-1的话就会变成aaaa011...可以将最低位的1消除
//如果n&(n-1) = 0的话就说明只有一个1且被消除
//注意与运算的优先级
if ((n & (n - 1)) == 0) {
return true;
}
else
return false;
}
2.位1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
提示:
请注意,在某些语言(如
Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。
示例 1:
输入:00000000000000000000000000001011 输出:3 解释:输入的二进制串
00000000000000000000000000001011 中,共有三位为 ‘1’。
int hammingWeight(uint32_t n) {
int sum = 0;
// while (n) {
// sum++;//1的个数加1
// //n >> 1;//逻辑右移一位
// n = n & (n - 1);
// }
//y也可以1与原数进行与操作,结果为1说明为1 1111...111(32wei)
for (int i = 0; i < 32; i++) {
//右移i位,每一位都和1进行与操作
if (((n >> i) & 1) == 1) {
sum++;
}
}
return sum;
}
day8—位运算
1.颠倒二进制位
颠倒给定的 32 位无符号整数的二进制位。
提示:
请注意,在某些语言(如
Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数
-1073741825。示例 1:
输入:n = 00000010100101000001111010011100 输出:964176192
(00111001011110000010100101000000) 解释:输入的二进制串
00000010100101000001111010011100 表示无符号整数 43261596,
因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 示例 2:输入:n = 11111111111111111111111111111101 输出:3221225471
(10111111111111111111111111111111) 解释:输入的二进制串
11111111111111111111111111111101 表示无符号整数 4294967293,
因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。
uint32_t reverseBits(uint32_t n) {
int num[32];
uint32_t m = 0;//直接放到无符号整数中
//获取最后一位并且赋值给数组
//取末k位 x&(1<<k)
//e = e & (1 << 0) & 1;
// 按顺序读出n的每一位(最低位),并加到当前ans的结果中。
// 然后将ans整体左移,将之前获得的值整体上移一位,这里就实现了逆序。
// 对于n,就整体下移一位,右移一位,为下一次读最低位做准备。
for(int i = 0;i<32;i++){
m<<=1;//先左移m,左移就是相当于乘2,就是在与n的最后一位相加前先乘以2
m |= n&1;//和1进行运算,0011 & 0001 = 0001;这样可以获得1的个数,并且左移以后相应增大
//printf("%d ",m);
n>>=1;
}
return m;
}
2.只出现一次的数字
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1] 输出:1 示例 2 :
输入:nums = [4,1,2,1,2] 输出:4
int singleNumber(int* nums, int numsSize) {
//1.不使用位运算,但是这种情况会有数字小于零的情况
// int a[30050] = {0};
// if(numsSize==1){
// return nums[0];
// }
// for(int i = 0;i<numsSize;i++){
// a[nums[i]]++;//会有数组内元素小于0的情况,不适用
// }
// printf("%d %d ",a[0],a[1]);
// int num = 0;
// for(int i = 0;i<numsSize;i++){
// if(a[i]==1){//因为a数组内存的就是num[i]
// num = i;
// break;
// //return i;
// }
// }
// return num;
//2.位运算
//因为题目中给了先决条件,只有两个元素重复,除掉重复的元素还剩1个
//因为有相同的元素可以对其进行异或运算,相同得0,不同得1
//因此对所有元素进行异或运算即可
int num = 0;
for (int i = 0; i < numsSize; i++) {
num ^= nums[i];// ^ 是异或运算
}
return num;
}