【C语言】找单身狗问题

news2024/10/9 2:23:07

一.找单身狗问题初阶

1.问题描述

一个数组中只有一个数字出现一次,其他所有数字都出现了两次.编写一个函数,找出这个只出现一次的数字.

例如:

有数组的元素是:1,2,3,4,5,1,2,3,4

只有5出现了一次,要找出5.


2.解题思路

常规思路:

在常规思路中,我们首先想到的肯定是使用两层循环嵌套的方式遍历整个数组,

如果在遍历的过程中,有数字找到了和它相同的数字,那么终止循环,换下一个数字遍历,

直到找出那个遍历完整个数组都没有找到与它相同的数为止.

由该思路得出的代码如下:

#include<stdio.h>
int Find_single_dog(int* str, int sz)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz; i++)
	{
		int num = str[i];
		for (j = 0; j < sz; j++)
		{
			if (i == j)
			{
				continue;//当遍历到它自身时,该次循环无效,我们直接跳出本次循环
			}
			if (str[i] == str[j])
			{
				break;//当遍历到和它相同的数时,证明他不是"单身狗",终止循环,寻找下一个数
			}
		}
		if (j == sz)
		{
			return str[i];//当遍历完整个数组时还没有找到和它相等的数,则该数字即为"单身狗"
		}
	}

}
int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4 };

	int sz = sizeof(arr) / sizeof(arr[0]);

	int single_dog=Find_single_dog(arr, sz);

	printf("%d", single_dog);

	return 0;
}

代码运行结果:

虽然该思路有效的完成了我们的要求,但该代码的循环次数是非常多的,

设数组一共有n个元素,则循环次数就几乎等于:n^n.

因此这种方法的时间复杂度非常高,程序的运行效率很低.


进阶思路:

在C语言中有一个异或(^)逻辑运算符,我们可以利用它的自反性质来找出"单身狗".

如果有对异或(^)还不是很了解的朋友可以先移步这篇博客,了解一下关于异或的一些性质,有助于理解后面的操作.【C语言】异或(^)操作符详解

先将文章里面的部分内容截出方便我们后续使用:

异或的运算法则(部分):

接下来我们画图来解释一下异或操作的步骤:

可以发现,凡是出现过两次的数字,两两异或后都变成了0,而唯一的只出现了一次的数字,与0异或的结果仍然是它本身,这说明整个数组相异或的结果恰好就是我们要找的"单身狗".

理解了进阶的思路后,代码的编写就很容易了,如下:

//进阶思路:利用异或操作的自反性来找出"单身狗"
int Find_single_dog(int* str, int sz)
{
	int num = 0;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		num ^= str[i];
	}
	return num;
}

int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4 };

	int sz = sizeof(arr) / sizeof(arr[0]);

	int single_dog = Find_single_dog(arr, sz);

	printf("%d", single_dog);

	return 0;
}

代码运行测试:

可以看到,该代码同样成功得到了我们想要的结果,并且当数组中有n个元素时,代码循环的次数为n,比常规思路中的n^n的时间复杂度简化了不少,运行效率也非常高.


二.找单身狗问题进阶

1.问题描述

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次.编写一个函数,找出这个两个只出现一次的数字.

例如:

有数组的元素是:1,2,3,4,5,1,2,3,4,6

只有5和6出现了一次,要找出5和6.


2.解题思路

常规思路:

在常规思路中,我们同样是使用两层循环嵌套的方式遍历整个数组,

如果在遍历的过程中,有数字找到了和它相同的数字,那么终止循环,换下一个数字遍历,

直到找出遍历完整个数组都没有找到与它相同的数,将这个数打印/存储,

再继续换下一个数遍历,寻找下一个"单身狗".

由该思路得出的代码如下:

void Find_single_dog(int* str, int sz)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz; i++)
	{
		int num = str[i];
		for (j = 0; j < sz; j++)
		{
			if (i == j)
				continue;
			if (str[i] == str[j])
				break;
		}
		if (j == sz)
			printf("%d ", str[i]);
	}
}

int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4,6};

	int sz = sizeof(arr) / sizeof(arr[0]);

	Find_single_dog(arr, sz);

	return 0;
}

运行结果:

可以发现,在进阶问题中,常规思路和初阶问题的常规思路复杂度几乎没有区别,效率同样很低.


进阶思路:

先来观察数组:

int arr[]={1,2,3,4,5,1,2,3,4,6};

我们把这几个数组元素摘出来,便于观察:

接下来就是要解决问题了,首先我们想到的是,能不能将这些元素分成两组,
当然最主要的还是将5和6这两个单身狗分开,并且保证每组剩余的数是成对出现的:
如:

1 1 3 3 5  (第一组)
2 2 4 4 6  (第二组)
这样的话,我们就可以分别对第一组和第二组使用刚才初阶问题中的全部相异或的方法来得到5和6.
那么分组时,最重要的就是需要找出两个单身狗(5和6)的区别:
在本例中,显而易见,5是奇数,6是偶数,
也就是说,5的二进制位最低位一定为1,而6的二进制位最低位一定为0.
那么我们就可以将二进制位的最低位是否为1作为分组的依据,进而将数组的全部元素按该条件进行分组就可以达到我们的目的了.
但这样做的话还有一个问题,那就是当单身狗是6和8的时候呢?它们的二进制末位都是0时,该如何将它俩区分呢?
这时我们可以尝试将两个单身狗异或一下,就能找到其中的规律.
如:

6的二进制表示为  0 1 1 0
8的二进制表示为  1 0 0 0               根据异或运算的规则:相同取0,相异取1

它们异或的结果为 1 1 1 0


可以发现,除了末位,6和8异或后倒数第二位,倒数第三位和倒数第四位的结果都为1,说明这三位上它们的二进制都不相同.
那么我们就可以用这三位的任意一位来作为分组的依据,就可以将6和8分到不同的组中了.

因此,我们在最开始的时候将数组中的所有元素相异或,得到的其实就是两个单身狗相异或的结果,

然后将该结果的二进制位从最低位开始检索,直到找到为"1"的那一位,记录下这一位,并以此作为分组的依据,将数组元素分为两组后分别相异或,得到的两个结果就是要找的两个单身狗.

由该思路所得代码如下:

//进阶思路
void Find_single_dog(int* str, int sz)
{
	int i = 0;
	int num = 0;
	for (i = 0; i < sz; i++)
		num ^= str[i];
	//计算num的哪一位是1,并记录下来
	int pos = 0;
	for (i = 0; i < 32; i++)
	{
		if (((num >> i) & 1) == 1)
		{
			pos = i;
			break;
		}
	}
	int single_dog1 = 0;
	int single_dog2 = 0;
	for (i = 0; i < sz; i++)
	{
		if (((str[i] >> pos) & 1) == 1)//以第pos位是否为1为条件,将元素分为两组分别异或得出结果
			single_dog1 ^= str[i];
		else
			single_dog2 ^= str[i];
	}
	printf("%d %d\n",single_dog1,single_dog2);
}

int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4,6 };

	int sz = sizeof(arr) / sizeof(arr[0]);

	Find_single_dog(arr, sz);

	return 0;
}

运行结果:

当数组的元素为n时,可得进阶的时间复杂度约等为:2n+32.相比常规思路的n^n效率高了不少.

因此在后续的类似找"单身狗"的问题中,希望大家可以多多使用异或的方式来提升查找的效率.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/994410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯云新用户有哪些优惠政策和专属活动?

腾讯云作为中国领先的云计算服务提供商&#xff0c;一直在为用户提供优质、高效且具有竞争力的服务。对于新用户&#xff0c;腾讯云更是诚意满满&#xff0c;推出了一系列优惠政策和专属活动。本文将详细介绍腾讯云新用户的优惠政策和专属活动&#xff0c;帮助大家更好地了解如…

ArcGIS 10.6安装教程!

软件介绍&#xff1a;ArcGIS是一款专业的电子地图信息编辑和开发软件&#xff0c;提供一种快速并且使用简单的方式浏览地理信息&#xff0c;无论是2D还是3D的信息。软件内置多种编辑工具&#xff0c;可以轻松的完成地图生产全过程&#xff0c;为地图分析和处理提供了新的解决方…

android注解之APT和javapoet

前言 前面我们已经讲过注解的基本知识&#xff0c;对于注解还不太了解的&#xff0c;可以去看一下之前的文章&#xff0c; android 注解详解_袁震的博客-CSDN博客。 之前我们在讲注解的时候&#xff0c;提到过APT和JavaPoet&#xff0c;那么什么是APT和JavaPoet呢&#xff1…

pprof火焰图性能优化

pprof火焰图性能优化 火焰图&#xff08;flame graph&#xff09;是性能分析的利器,在go1.1之前的版本我们需要借助go-torch生成,在go1.1后go tool pprof集成了此功能,今天就来说说如何使用其进行性能优化 在你启动http server的地方直接加入导入: _ “net/http/pprof” 获取…

如何在 7 分钟内黑掉 40 个网站

这将详细讲述我如何侵入托管 40 个(这是一个确切数字)网站的服务器以及我的发现。 注意:需要一些必备的 CS 知识才能理解本文的技术部分。 一位朋友给我发消息说他的网站发现了XSS 漏洞,希望我进一步查看。这是一个重要的阶段,因为我倾向于要求他正式表示我已获得他的许可…

Mojo 摸脚语言,似乎已经可以安装

文章原地址&#xff1a;https://i.scwy.net/it/2023/090821-mojo/ Mojo 吹得很凶&#xff0c;面向AI编程&#xff0c;甩Python几十条街&#xff0c;融资上亿.... 但无缘一试&#xff0c;在Win和Ubuntu上试都不能通过。 由 LLVM 和 Swift 编程语言的联合创始人 Chris Lattner…

荣耀崛起阵容推荐,荣耀崛起最强阵容

今天给大家带来的荣耀崛起阵容推荐是新手阵容推荐&#xff0c;以核心输出为点&#xff0c;由点及面&#xff0c;来展开叙述阵容&#xff01; 关注【娱乐天梯】&#xff0c;获取荣耀崛起0.1折内部福利号 荣耀崛起最强阵容兽族战神流&#xff1a; 此阵容是以战士为核心&#xff0…

pycharm使用

在使用pycharm时&#xff0c;有时一个回车或者一个tab键&#xff0c;缩进的长度不符合预期可以调整设置tab键缩进的长度&#xff1a; 平时工作中&#xff0c;不同的人在编辑代码缩进的时候&#xff0c;有的人喜欢按四个或者六个空格&#xff0c;有的人喜欢按tab键&#xff0c;而…

网络变压器/网络滤波器(脉冲变压器)选型/定制要明确的要求

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;1,做电路板项目开发的工程师有时会遇到开发标的的空间限制&#xff0c;要对电子元器件的占用空间进行优化&#xff0c;同样电性参数的产品有的产品选择性比较小,只有部分产品有可选择性,这就给工程师进行板子空间优…

OJ练习第167题——单词接龙

单词接龙 力扣链接&#xff1a;127. 单词接龙 题目描述 字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> … -> sk&#xff1a; 每一对相邻的单词只差一个字母。 对于 1 < i < k 时&…

ES快速学习

ES 用于&#xff1a;存储、分析、检索 的工具 组成及与类比到mysql组成 倒排索引

Redis 介绍安装

数据库 关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。 SQL 语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语言&#xff0c;用于执行对关系型…

Spring Bean的生命周期及三级缓存流程

Spring Bean 的生命周期说起来其实就三个大块&#xff1a;实例化Bean -> 设置属性(注入和装配) -> 初始化 -> 使用Bean -> 销毁Bean 这个很好理解&#xff0c;但是内部是怎么样注入&#xff0c;初始化以及销毁&#xff1f;经历怎么样的过程呢&#xff1f;追随这些问…

LeetCode(力扣)332.重新安排行程Python

LeetCode332.重新安排行程 题目链接代码 题目链接 https://leetcode.cn/problems/reconstruct-itinerary/ 代码 class Solution:def backtracking(self, tickets, used, cur, result, path):if len(path) len(tickets) 1:result.append(path[:])return Truefor i, ticket…

软件设计开发笔记4:QT操作SQLite数据库

有时候我们需要在软件中记录一些历史数据以便于对数据的查询。而我们希望软件不能太复杂&#xff0c;体量也不要太大&#xff0c;这个时候就需要如SQLite这样轻量级的数据库。这篇中我们就来讨论如何在使用QT开发应用是操作SQLite数据库。 0、概述 SQLite是一款开源、轻量级、…

SpringBoot原理-自动配置-原理分析-@Conditional

前言 在自动配置类中声明Bean的时候&#xff0c;除了在方法上添加Bean注解&#xff0c;还会加上Conditionalxxx的注解&#xff08;该注解都是条件装配的注解&#xff09; Conditional 作用&#xff1a;按照一定的条件进行判断&#xff0c;在满足给定条件后才会注册对应的bea…

CSP 202203-1 未初始化警告

答题 要注意是xi和yi的范围&#xff0c;yi可以是0为常数。 #include<iostream> using namespace std;int main() {int n,k;cin>>n>>k;bool*initializenew bool[n]{false};int result0,x,y;while(k--){cin>>x>>y;if(y&&!initialize[y-1…

E. Hanging Hearts

Problem - E - Codeforces 思路&#xff1a;我们考虑用树形dp&#xff0c;用f[i][0]表示以i为根&#xff0c;并且当前节点不在最长上升子序列中&#xff0c;用f[i][1]表示以i为根&#xff0c;当前节点在最长上升子序列中&#xff0c;那么f[i][0]max(f[j][0],f[j][1])&#xff0…

4年经验来面试20K的测试岗,连基础都不会,还不如招应届生!

公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在10-20k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。 看简历很多都是3、4年工作经验&#xf…

使用Python 进行分析

在当今竞争激烈的互联网时代&#xff0c;对于网站的SEO优化至关重要。本文将介绍一种强大的秘密武器&#xff1a;使用Python 进行竞争对手网站分析。通过这种技术&#xff0c;您可以深入了解竞争对手的网站结构、关键词排名和优化策略&#xff0c;为您的SEO优化工作提供有力支持…