算法很美
位运算的奇淫巧计
- 判断奇偶性
- 所有的奇数 & 1 都是 1
- 所有的偶数 & 1 都是 0
- 交换两个整数的值
- ^三次。因为任何一个数异或自身为0,异或0为自身。
int a = 32;
int b = 43;
int c = a^b;
b=c^b;
a=c^a;
printf("%d%d",a,b);
- 例题解析
某个数组中除了某一个元素出现了一次外,其余元素皆出现了两次,请求出那个数
- 思路:a ^ a = 0, a ^ 0 = a;
#include<stdio.h>
int main(){
int n = 0;
scanf("%d",&n);
int arr[n];
int temp=0;
for(int i = 0 ; i< n;i++){
scanf("%d",&arr[i]);
}
for(int i = 0 ; i< n;i++){
temp^=arr[i];
}
printf("%d",temp);
return 0;
}
1-1000这1000个数放在大小为1001 的数组中
现有1001个数,其中有一个数重复,其余数皆只有一个,求重复的数
- 思路:a[i]为(0-1000),则a[i]^(0-1000),消去了出现了2次的数
#include <stdio.h>
#include <stdlib.h>
int main(){
int n = 1001;//n为数组的大小
int arr[n];
int i = 0;
int temp=0;
for(i=0;i<n-1;i++){
arr[i]=i+1;
}
arr[n-1]=rand()%n+1;//生成1-1000的随机树
for(i=1;i<=n-1;i++){
temp=temp^i;
}
for(i=0;i<n;i++){
temp=temp^arr[i];
}
printf("%d",temp);
}
输入一个整数,求该整数二进制中所包含的1的个数
- 思路①: 辗转相除,求余数
#include <stdio.h>
int main(){
int n = 0;
scanf("%d",&n);
int temp;
int count = 0;
while(n!=0){
temp = n%2;//取余数
n/=2;
if(temp==1){
count ++;
}
}
printf("%d",count);
return 0;
}
- 思路②:x&(x-1) 消掉最近低位的1
#include <stdio.h>
int main(){
int n = 0;
scanf("%d",&n);
int count = 0;
while(n!=0){
n=(n-1)&n;
count++;
}
printf("%d",count);
return 0;
}
将整数的二进制的奇偶位互换
- 思路:用i&1010保留偶数位的1,用i&0101保留奇数位的1
#include<stdio.h>
int main(){
int n = 0;
scanf("%d",&n);
int ou = n&0xaaaaaaaa;//二进制 1010 1010 1010 1010 1010 1010 1010 1010
int ji = n&0x55555555;//二进制 0101 0101 0101 0101 0101 0101 0101 0101
printf("%d",(ou>>1)^(ji<<1));
return 0;
}
将十进制0~1的double类型小数,转换为二进制小数,若小数位数超过32位,则报error
/*
将0~1之间的小数转化为二进制,若二进制位数超过21位,则报error
*/
#include<stdio.h>
int main(){
char arr[32];
double n;
scanf("%lf",&n);
int i = 0;
while(n>0){
n*=2;
if(n>=1){
arr[i]=1;
n-=1;
}else{
arr[i]=0;
}
i++;
if(i>32){
printf("error");
return 0;
}
}
printf("0.");
for(int j = 0 ; j<i;j++){
printf("%d",arr[j]);
}
return 0;
}
思考:有一组数,其中有一个数出现1次,其余数出现k次,求出现1次的数 ()
- 思路:k进制的k个数不进位相加位0
#include<stdio.h>
int main(){
printf("暂时不会");
return 0;
}
递归
- 什么是递归
- 编程语言中,函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。递归函数不能定义为内联函数。
- 在数学上,关于递归函数的定义如下:对于某一函数f(x),其定义域是集合A,那么若对于A集合中的某一个值X0,其函数值f(x0)由f(f(x0))决定,那么就称f(x)为递归函数。
- 案例解析:
小白上楼梯(递归设计)
小白正在上楼梯,楼梯有n阶台阶,
小白一次可以上1阶,2阶或者三阶。
实现一个方法,计算小白有多少种走完楼梯的方式。
#include<stdio.h>
int fun(int len){
if(len==0){
return 0;
}
if(len==1){
return 1;
}
if(len==2){
return 2;
}
if(len==3){
return 4;
}
if(len>3){
return fun(len-1)+fun(len-2)+fun(len-3);
}
}
int main(){
int n = 0;
scanf("%d",&n);
printf("%d",fun(n));
return 0;
}
查找与排序
- 二分查找
- 二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
- 要求:
- 必须采用顺序存储结构。
- 必须按关键字大小有序排列。
用递归形式实现二分查找
#include<stdio.h>
int binSearch(int arr[],int begin,int end,int key){
int mid=begin+((end-begin)>>1);//注意这里的mid取值,为什么不是(begin+end)/2?
if(end<begin){
return -1;
}
if(arr[mid]<key){
binSearch(arr,mid+1,end,key);//注意参数mid+1,为什么是mid+1?
}
else if(arr[mid]>key){
binSearch(arr,begin,mid-1,key);//注意参数mid-1
}else{
return mid;
}
}
int main(){
int arr[]={1,3,5,7,14,23,90};
printf("%d",binSearch(arr,0,7,3));
return 0;
}
- 二分法(改造)
把一个数组最开始的若干元素搬到数组的末尾,我们称之为数组的旋转。
输入一个递增排序的数组的一个旋转,输出旋转数组的最小值。
例如{34512}为{12345}的一个旋转,该数组最小值为1.
#include<stdio.h>
#define MAX 10
int binarySearch(int arr[],int begin,int end){
if(arr[end]>=arr[begin]){ //数组未旋转
return arr[begin];
}
int mid = begin+((end-begin)>>1);
if(arr[mid]>arr[begin]){
binarySearch(arr,mid,end);
}else if(arr[mid]<arr[begin])
{
binarySearch(arr,begin,mid);
}else{
return arr[end];
}
}
int main(){
int arr[MAX]={79,2,3,4,5,7,12,43,65,76};
printf("%d",binarySearch(arr,0,MAX-1));
return 0;
}
- 希尔排序
- 希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
#include<stdio.h>
#define MAX 10
void shellSort(int arr[]){
for(int interVal =MAX/2;interVal>0;interVal/=2){
for(int i=interVal;i<MAX;i++){
int target = arr[i];
int j = i-interVal;
while(j>-1&&arr[j]>target){
arr[j+interVal]=arr[j];
j-=interVal;
}
arr[j+interVal]=target;
}
}
}
int main(){
int arr[MAX]={12,43,54,6,3,12,43,5,65,76};
shellSort(arr);
int i = 0;
while(i<MAX){
printf("%d\t",arr[i]);
i++;
}
return 0;
}
- 分治法
- 分治法可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化。
- 分治法的精髓:
- 分–将问题分解为规模更小的子问题;
- 治–将这些规模更小的子问题逐个击破;
- 合–将已解决的子问题合并,最终得出“母”问题的解
- 归并排序
树及二叉树
- 二叉树的建立(三种遍历方法)
#include <stdio.h>
#define MAX 9
//先序遍历
void preArray(int *arr,int index){
if(index>=MAX) return;
printf("%d\t",arr[index]);
preArray(arr,2*index+1);
preArray(arr,2*index+2);
}
//中序遍历
void inArray(int *arr, int index){
if(index>=MAX) return;
inArray(arr,2*index+1);
printf("%d\t",index);
inArray(arr,2*index+2);
}
//后续遍历
void afterArray(int *arr, int index){
if(index>=MAX) return;
afterArray(arr,index*2+1);
afterArray(arr,index*2+2);
printf("%d\t",index);
}
int main(){
int arr[MAX]={78,56,34,43,4,1,15,2,23};
preArray(arr,0);
inArray(arr,0);
afterArray(arr,0);
return 0;
}
- 堆排序
#include<stdio.h>
#define MAX 9
void swap(int &a,int &b){
int temp = a^b;
a=temp^a;
b=temp^b;
}
void maxheapTree(int *arr,int len,int index){
int left = 2*index+1;
int right = 2*index+2;
int max=left;
if(left>=len) return;
if (right>=len){
if(arr[index]<arr[max]) swap(arr[index],arr[max]);
return;
}
if(arr[left]<arr[right]){
max=right;
}
if(arr[index]<arr[max]) swap(arr[index],arr[max]);
maxheapTree(arr,len,max);
}
void heapSort(int *arr,int len){
int index = len/2-1;
for(;index>=0;index--){
maxheapTree(arr,len,index);
}
}
void sort(int *arr,int len){
while (len>0)
{
heapSort(arr,len);
swap(arr[0],arr[--len]);
}
}
int main(){
int a[MAX]={2,23,43,4,56,1,15,34,78};
sort(a,MAX);
for (int i = 0; i < MAX; i++)
{
printf("%d\t",a[i]);
}
return 0;
}