找单生狗,模拟atoi,模拟offsetof,交换奇偶位

文章目录

  • 1. 找单身狗
    • 思路
    • 代码
  • 2. 模拟实现atoi
    • 思路
    • 代码
  • 3. 实现offsetof宏
    • 思路
    • 代码
  • 4. 交换奇偶位
    • 思路
    • 代码

1. 找单身狗

找单生狗,模拟atoi,模拟offsetof,交换奇偶位_第1张图片

思路

要找出不同的数首先想到异或,由于异或的性质,相同的数异或为0,0异或任何数等于本身,
因此可以使用异或解决这题:
联想到如果只有一个数出现一次,那么将这组数全部异异或一遍最后的结果就是出现一次的数,这题有两个数出现了一次,所以可以将这一组数分成两组,每组都是只有一个数出现一次,每一组的元素全部异或,最后2组的2个结果就是只出现一次的两个数
分割线---------------------------------------------------------------------------------
关键问题变成了通过什么条件能够成功将这两个数分在不同组,所以我们需要找到能够区别这两个数的一个标记,有没有很熟悉?什么可以区别2个数,什么操作符专门为区别而生?什么操作符专门为找不同而生?没错,它就是异或
如果两个数的对应的某个二进制位异或为1,那么这两个数对应的位肯定一个是0,另一个是1,于是我们就可以通过根据这一位来分组,这一位为0的在一组,这一位为1的在另一组,于是就有以下代码

代码

//找出两个只出现一次的数字
#include 
/// 
/// res用来接受两个单身狗
/// n是两个单身狗的异或
/// pos用来表示两个单身狗不同的位
/// 
/// 
/// 
void FindArr(int* arr, int sz)
{
	int res[2] = {0};
	int n = arr[0];
	int pos = 0;
	//得到两个单身狗的异或
	for (int i = 1; i < sz; i++)
	{
		n ^= arr[i];
	}
	//找到异或结果为1的位
	for (int i = 0; i < 32; i++)
	{
		if (1 == ((n >> i) & 1))
		{
			pos = i;
			break;
		}
	}
	//根据两个单身狗的pos位不同分组,每组分别异或
	for (int i = 0; i < sz; i++)
	{
		if (1 == ((arr[i] >> pos) & 1)) res[0] ^= arr[i];
		else res[1] ^= arr[i];
	}
	printf("单身狗->%d\n单生狗->%d", res[0], res[1]);
}
int main()
{
	int arr[] = { 1,2,3,4, 5,1,2,3, 4, 6 };
	FindArr(arr, sizeof(arr) / sizeof(int));
	return 0;
}

2. 模拟实现atoi

atoi函数介绍
找单生狗,模拟atoi,模拟offsetof,交换奇偶位_第2张图片

  • atoi函数的功能是将字符串转换为整数
  • 首先丢弃最开始的空白,直到找到第一个数字字符
  • 如果第一个非空白字符不是数字字符也不是’+‘’'-,返回结果为0
  • 第一个非空白字符是’-'表示返回的是负数
  • 该字符串可以在构成整数的字符之后包含其他字符,这些字符将被忽略并且对此函数的行为没有影响。

思路

由于atoi函数转化失败会返回0,转换成功也有可能返回0,因此为了区分0是失败产生的还是成功产生的,定义枚举常量来体现失败还是成成功
不合法的情况有以下几种

  1. 转入的是空串
  2. 第一个非空白字符不是符号位或整数字符
  3. 符号位后的字符不是整数字符
  4. 转换的结果超出了int 所表示的范围

代码

//模拟atoi
#include 
#include 
#include 
#include 
#include 
enum State
{
	VALID,
	UNVALID
};
enum State state = 0;//开始默认合法

int Atoi(const char* str, char* result)
{
	assert(str);

	//传入空串
	if (*str == '\0')
	{
		state = 1;
		strcpy(result, "传入的是空字符串");
		return 0;
	}

	int len = strlen(str);
	int i = 0;
	long long res = 0;
	int sign = 1;
	while (isspace(str[i])) i++; //找到第一个非空白字符
	//第一个非空白字符不合法
	if (str[i] != '+' && str[i] != '-' && !isdigit(str[i]))
	{
		state = 1;
		strcpy(result, "第一个非空白字符不合法");
		return 0; 
	}
	//判断正负
	if (str[i++] == '-') sign *= -1; 
	
	//符号位后的字符不是数字字符
	if (!isdigit(str[i]))
	{
		state = 1;
		strcpy(result, "符号位后的字符不合法");
		return 0;
	}

	//找到第一个非数字字符
	while (isdigit(str[i]) && str[i] != '\0')
	{
		if (res > INT_MAX)
		{
			//结果超出INT所表示的范围
			state = 1;
			strcpy(result, "转换结果超出了INT的范围");
			return 0;
		}
		res = 10 * res + str[i] - '0';
		i++;
	}
	return sign * res;
}

int main()
{
	char str[100];
	//记录失败的原因
	char result[100];
	fgets(str, 100, stdin);
	int n = Atoi(str, result);
	if (state == 1)
	{
		printf("转换失败->%s\n", result);
	}
	else
	{
		printf("转换成功->%d\n", n);
	}
	return 0;
}

3. 实现offsetof宏

找单生狗,模拟atoi,模拟offsetof,交换奇偶位_第3张图片

  • 这个函数形式的宏返回成员成员在数据结构或联合类型类型中以字节为单位的偏移值。

思路

找单生狗,模拟atoi,模拟offsetof,交换奇偶位_第4张图片
想要求出某个成员的偏移量,可以用该成员的地址减去结构体变量的起始地址,由于实现的是一个宏,没法直到结构体变量的地址,因此关键在于如何知道结构体变量的地址
我们虽然不知道结构体变量的地址,但是我们可以假设结构体变量在地址为0处,这样的话该结构体某个成员的地址就是该成员的偏移量

代码

//写一个宏,实现offsetof
#include 
//宏的特点,不在乎参数的类型
//假设0处的地址为该结构体类型的地址,通过->和&找到所求偏移量的成员地址,将地址强转为uint类型
#define OFFSETOF(type, number)  (size_t)(&(((type*)0)->number))
struct People
{
	int age;
	double weight;
};
int main()
{
		printf("age->%d\nweight->%d\n",
		OFFSETOF(struct People, age), OFFSETOF(struct People, weight));
		return 0;
}

注意:在这个特定的用例中,->实际上是用来计算结构体或类成员的偏移量的语法,而不是用来解引用一个指针的语法,因此不会出现解引用空指针这个问题


4. 交换奇偶位

在这里插入图片描述

思路

交换相邻的奇偶位一共三步

  1. 取出所有奇数位(偶数位为0),左移一位使得奇数位在偶数位上
  2. 取出所有偶数位(奇数位为0),右移一位使得偶数位在奇数位上
  3. 将1、2的结果相加

1中另偶数位为0的目的就是为了相加后偶数位恰好是原来的奇数位,2中同理

代码

#include 
//&0xaaaaaaaa可以保留偶数位并且另奇数位为0
//&0x55555555可以保留奇数位并且另偶数位为0
#define SWAP_BITS(x) x = (((x) & 0xaaaaaaaa) >> 1) + (((x) & 0x55555555) << 1)
int main()
{
	unsigned int n = 5;
	SWAP_BITS(n);
	printf("%u\n", n);
}

tips:

  • 可以将+替换成|
  • 如果要交换long long 类型数据,需要用0xaaaaaaaaaaaaaaaa替换0xaaaaaaaa,0x5555555555555555替换0x55555555

你可能感兴趣的:(题,算法)