#include <iostream>
#include <vector>
#include "algorithms.h"
using namespace std;
void print()
{
cout<<" hello world"<<endl;
}
long StringToInt(char *ch) //功能测试,边界测试,负面测试(不符合常规代入参数)
{ // 前面带 ‘+’ ‘-’代表正负,还有字符中出现字母需要判断
if(!ch) return -1; // 就是不是数字的字符
long num = 0;
bool isPositive = false;
if(*ch == '+') //
{
isPositive = true;
++ch;
}
else if(*ch == '-') //
{
isPositive = false;
++ch;
}
while(*ch)
{
if('0'<=*ch && *ch<='9') // '0'<=*ch<='9' 这样写就是 或 的关系
{
num = num * 10 + (*ch - '0');
++ch;
}
else
return -1;
}
return isPositive ? num : -num ; //前面是bool 若真整体的值就是num,即前者
}
// [9/29/2013 qingezha] 正数的开方 主要采用二分法,一个为0.0001 ,另外一个为value
// mid 为二者的平均值,如果平均值的平方大于value则mid取上一次mid和0.0001之间的平均值
float my_sqrt(float value)
{
float low = 0.0000001;
float high = value>1?value:value+1;
float mid = 0.0;
float min = 0.0;
do
{
mid =(high + low)/2;
min = mid * mid;
if(min>value)
high = mid;
else
low = mid;
} while (abs(value - min)>=0.000001);
return mid;
}
// [9/30/2013 qingezha] // lowbit表示的是某个数从右往左扫描第一次出现1的位置
int lowbit(int x)//输出x 的低位中的第一个1 位置
{
return x&~(x-1); //x 与 负x 相与 找到
}
void find(int *arr,int len)
{
int xor = 0;
int flips = 0;
for (int i=0;i<len;++i)
xor ^= arr[i];
// 三个数两两的异或后lowbit有两个相同,一个不同,可以分为两组 //正确
for(int i=0;i<len;++i)
flips ^=lowbit(xor ^ arr[i]);
// 表示的是:flips=lowbit(a^b)^lowbit(a^c)^lowbit(b^c)
int b = 0; // 假设三个只出现一次的其中一个数为b
for(int i=0;i<len;++i)
if(xor ^ arr[i] == flips)
b = arr[i];
cout<<b<<endl;
//test/
//int arr[] = {1,5,7,1,5,7,12,11,13}; //共 11 个
//find(arr,9);
//
}
// [9/30/2013 qingezha] Union-Find 并查集
// 初始化所有的结点用整数表示,比如(0--n-1),在处理输入pair之前,他们分属于不同的组,每个结点都是孤立的
// 可以用数组表示这种关系,数组的index表示结点,而对应的值表示组号
int const NODE_NUM = 1001;
int count_set = NODE_NUM;
int arr[NODE_NUM]={0};
void UF_init() //初始化
{
for (int i=0;i<NODE_NUM;++i)
arr[i] = i;
}
int find(int p) //查找这个结点属于 哪个组
{
if(p>=0&&p<NODE_NUM)
return arr[p];
else
return -1;
}
bool connect(int p,int q) //判断是否连接
{
if(p>=0 && p<NODE_NUM && q>=0 && q<NODE_NUM)
return arr[p]==arr[q];
else
return false;
}
int count() //组的个数
{
return count_set;
}
bool union_two(int p,int q) //合并2个,使之为一个组
{
if(p>=0 && p<NODE_NUM && q>=0 && q<NODE_NUM)
{
if(arr[p]==arr[q])
return true;
else
for(int i=0;i<NODE_NUM;++i)
if(arr[i]==arr[p])
arr[i] = arr[q];
--count_set;
return true;
}
else
return false;
}
// [10/7/2013 qingezha].给定一个源串和目标串,能够对源串进行如下操作:
// 1).在给定位置上插入一个字符
// 2).替换任意字符
// 3).删除任意字符
// 写一个程序,返回最小操作次数,使得对源串进行这些操作后等于目标串。
// 例如:源串”hello”,目标串”lleo”,则通过3次修改可以使得源串变成目标串(删除’h',删除’e',在’o'之前插入字符’e')
//也可以通过找到最长公共字串,然后2个原来串的长度和减去公共字串的长度即可
//用f[i][j]表示要修改的最少次数 源头x[1...i] 目标y[1...j],如果x[i]==y[j] 则f[i][j]=f[i-1][j-1]
//如果不同,则可以用增,删,改,分别对应的f[i][j-1]+1,f[i-1][j]+1,f[i-1][j-1]+1,
int cal_distance(const char *sta,const char *stb)
{
if(sta == NULL || stb == NULL)
return 0;
int f[10+1][5+1]={0}; //这里可以用new一个一维数组,代替栈上的二维数组,因为栈上的编译时就确定长度,堆上的运行时才确定
//这里纯用于测试
for (int i=0;i<11;++i)
f[i][0]=i; //悲剧啊,这里误写成0 了
for (int i=0;i<6;++i)
f[0][i]=i; //悲剧啊,这里误写成0 了
int temp = 0;
for (int j=1;j<6;++j)
{
for (int i=1;i<11;++i) //j<6写成了i<6,以后要小心啊
{
if(sta[i]==stb[j])
f[i][j]=f[i-1][j-1];
else
{
temp = min(f[i-1][j-1]+1,f[i-1][j]+1);//这里是增,删,改对应的次数
f[i][j]=min(f[i][j-1]+1,temp);
}
cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
}
}
return f[10][5];
}
// [9/30/2013 qingezha] 链表倒置 循环与递归形式
// 一般形式,1—>2->3->4 现在1<-2<-3<-4 那么改变1的next的时候需要保存指向2的指针,然后同理处理2
// 需要保存的,用直译(见词知意)的表达出来比如:pre前面,next后面,cur当前,然后循环后赋新值
LinkPtrs reverse_link(LinkPtrs root) //头指针指向的是第一个元素
{
if(root == NULL)
return NULL;
LinkPtrs nextp = root->next; //第一个结点
LinkPtrs pre = root; //保留前端
LinkPtrs temp = NULL;
LinkPtrs reverseHead = NULL;
pre -> next = NULL;
while(nextp->next)
{
temp = nextp -> next; //先要保存下一个指针
nextp -> next = pre;
pre = nextp;
nextp = temp;
}
nextp -> next = pre;
reverseHead = nextp;
return reverseHead;
}
//链表倒置,切记 方法很巧!!!!!!!!!!!!!!!!!!
LinkPtrs reverse_link_recursive(LinkPtrs root)
{
if(root == NULL)
return NULL;
LinkPtrs cur,temp,revers_head;
if(root->next == NULL)
return root; //链表如果只有一个结点,那么直接返回第一个
else
{
cur = root;
temp = cur -> next; //temp 为2->3->4的头指针
//可以认为后面的都已经倒过来了,且认为revers_head 为倒过来的链表的头指针
//这样理解就容易多了
revers_head = reverse_link_recursive(temp);
temp -> next = cur;
cur -> next = NULL;
}
return revers_head;
}
// [9/30/2013 qingezha]实现一个函数,对一个正整数n,
// 算得到1需要的最少操作次数。操作规则为:如果n为偶数,将其除以2;如果n为奇数,可以加1或减1;一直处理下去。
//奇数的时候加1或减1,完全取决于二进制的后两位,如果后两位是10、00那么肯定是偶数,选择除以2,
//如果后两位是01、11,那么选择结果会不一样的,如果是*****01,那么选择减1,如果是*****11,那么选择加1,
//特殊情况是就是n是3的时候,选择减1操作。
int func(unsigned int n)
{
if(n==1)
return 1;
if(n%2==0)
return 1 + func(n/2);
if(n==3)
return 2; //3到1 共2步,先-1 后除以2,
if(n&2)
return 1 + func(n+1);
else
return 1 + func(n-1);
}
// [10/2/2013 qingezha] 数组子序列的个数,4,14,2,3和14,1,2,3都为4,13,14,1,2,3的子序列。 对于给出序列a,有些子序列可能是相同的,这里只算做1个,要求输出a的不同子序列的数量。
// 用一个数组记录出现2次或2次以上的数字,离当前数字最近的相同的数字的下标
// 比如1 2 3 4 2,一开始都为0,然后下标一次变为1 2 3 4,到新2 的时候 因为有2,所以要找到这个2的下标,才可以运算那个式子
// 所以用last_index记录数字的下标,里面是对应的arr[i],即要找的值2
#define Mod 1000000007
long sub_sequence(int *arr,int len) //整数数组不能检查越界,要已知长度
{
long sub_arr[120] = {0};
int last_index[120] = {0}; //初始值这里设的好
for (int iter=1;iter<=len;++iter)
{
switch(last_index[arr[iter-1]])
{
case 0:
{
sub_arr[iter] = 2 * sub_arr[iter-1]+ 1;
break;
}
default:
{
sub_arr[iter] = 2 * sub_arr[iter-1] - sub_arr[last_index[arr[iter-1]]-1];//上一个相同的数字的下标
}
}
last_index[arr[iter-1]] = iter; //这里写错了last_index[iter-1],每一次都更新,按次序递增
}
return sub_arr[len]; //这里写错了 sub_arr[len-1]
//test///
//int arr[20] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
//cout<<sub_sequence(arr,sizeof(arr)/sizeof(int));
//
}
// [10/3/2013 qingezha]已知线段长度,求覆盖最多的点数,(一个有序数组代表各个点)
int calculate_num(int *arr,int length,int seg_len)
{
if(arr == NULL||length<=0 || seg_len<=0)
return 0;
int rear = length - 1;
int front = 0;
int max_num = 0;
while(rear>=0) //从最后一个点出发,依次减去前面的点的值,判断差值,然后记录点的个数
{
front = rear;
while(front>=0)
{
if((arr[rear]-arr[front])<=seg_len)
--front;
else //这里要记住,跳出循环
break;
}
//换成while((arr[rear]-arr[front])<=seg_len) 更简洁
max_num = rear-front > max_num ? rear - front : max_num; //这里三元操作运算符
--rear;
}
return max_num;
//
// int arr[5] = {1,2,5,6,9};
// cout<<calculate_num(arr,5,2);
//
}
// [10/3/2013 qingezha] 真没想到可以从中间向两边出发,分别比较2边是否相同,注意回文个数是偶数/奇数情况
int getpalindrome(char *arr,int len)//获取最大的回文
{
int i = 0;
int j = 0;
int max = 0;
for (i=0;i<len;++i)
{
for(j=1;i+j<len&&i-j>=0;++j) //回文是奇数的情况
if(arr[i-j] !=arr[i+j])
break;
if(2*j-1>max)
max = 2*j - 1;
for (j=1;i-j>=0&&i+j+1<len;++j) //回文长度为偶数,认为与相邻的后面相同
if(arr[i-j]!=arr[i+j+1])
break;
if(i+j+1==len&&2*j + 2>max)
max = 2*j + 2;
if(i+j+1==len&&2*j - 2>max) //这里j在元素不等时跳出与for中条件不满足时跳出
max = 2*j - 2;
}
return max;
}
//另外一种方法是abcgoogleaba,可以进行这样的操作:#a#b#c#g#o#o#g#l#e#a#b#a#
//中间插入#或其他字符
int getpalindrome_ss(char *arr,int len)//获取回文的最大长度
{
int len2 = 2*len + 1;
int j = 0;
int i = 0;
int max = 0;
char *str = new char[len2];
for (int i=0;i<len;++i)
{
str[2*i] = '#';
str[2*i+1] = arr[i];
}
str[len2-1] = '#'; //中间插入“#”
for(i=0;i<len2;++i)
{
for(j=1;i+j<len2&&i-j>=0;++j)
if(arr[i-j] !=arr[i+j])
break;
if(2*j-1>max)
max = 2*j - 1;
}
delete[] str;
return max;
}
void get_n()
{
int num = 20; //控制输出个数
int i = 1;
int temp = 0;
while(1)
{
if(temp%3==2&&temp%5==3&&temp%7==2)
{
cout<<i<<" "<<temp<<endl;
++i;
if(i>num)
break;
}
++temp;
}
}
// [10/10/2013 qingezha]最长相同字符
int large_same(char *arr)
{
if(arr == NULL)
return -1;
int count = 0;
int max1 = 1;
while(*(arr+1)!=0 && *arr == *(arr+1))
{
++arr;
++max1;
}
if(*(arr+1)==0)
return max1; //这里出口很重要!!!!可以举例,比如aabb带进去试试,看看有没返回值
int max2 = large_same(++arr); //别忘 了 是++,就是下一个
return max2>max1?max2:max1;
}
// [10/10/2013 qingezha]没有重复出现的数字
unsigned int GetNotRepeatNum(unsigned int lValue)
{
int count[10]={0};
int temp = lValue;
int index = 0;
int i = 0;
bool isfind = true; //初始值为false 错了
while(1)
{
while(lValue>0) //这里出口1
{
index = lValue % 10;
count[index]++;
//if(count[index]>1) //这里出口2
// break;
lValue = lValue/10;
}
for (i=0;i<10;++i)
if(count[i]>1) //只要有一个就为false,就跳出
{ //另外跳出也有i<10的可能
isfind = false;
break;
}
//else
// isfind = true; //这里没写,错了
for (int i=0;i<10;++i)
count[i] = 0;
if(!isfind) //通过break出口
lValue=++temp;
if(i==10) //通过i<10出口
return temp;
}
}
// [10/6/2013 qingezha]遍历一个文件,里面元素个数不知道,让你设计一个算法遍历一遍,等概率的随机取出一个元素
// 可以这样:设现在遍历到第 i 个元素,现在判断如果 rand()%i为0则将返回值更新为第i个元素;否则不变
char get_equal_char(char *arr)
{
if(arr == NULL)
return NULL;
int i = 1;
char re_char = arr[0];
char *temp = arr;
while(*arr)
{
if(rand()%i==0) //这里牛逼
re_char = arr[i-1];
++i;
++temp;
}
return re_char;
}
// [10/7/2013 qingezha] 求出1…n之间的所有亲和数。
// 所谓亲和数,即存在数a和数b,a的所有真因子之和等于b,b的所有真因子之和等于a,则称a和b为一对亲和数。
// 例如220的真因子为:1、2、4、5、10、11、20、22、44、55、110,和为284;而284的真因子为:1、2、4、71、142,和正好为220。故220和284是一对亲和数。
// 现在设j 的真因子和为sum[j],那么j 可以被所有的因子整除的和为sum[j] ,其中可以整除就是关键
void print_Affsum(int n)
{
if(n<=0)
return;
int *sum = new int[n+1];
for (int i=1;i<n+1;++i)
sum[i]=1; //所有亲和数都有因子 1
for (int i=2;i<n/2;++i) //i 作为因子,所以最大也只能为 n/2
{
for (int j=2*i;j<n+1;j+=i)//j 为可以整除 i,所以递增为 i
{
sum[j] += i;
}
}
int i = 1;
while(i<n+1)
{
if(sum[i]<n+1&&i==sum[sum[i]]) //这里别忘记检测sum[i]的值,因为sum[sum[i]]可能越界
cout<<i<<" "<<sum[i]<<endl;
++i;
}
delete[] sum;
}
// [10/7/2013 qingezha]给出一个整型数组num[],和一特定值x,判断数组中是否存在一个或若干个元素之和为x,若存在则返回true,否则false。
bool is_sum(int *arr,int len,int x)
{
if(arr==NULL||len<1)
return false;
if(len==1)
return arr[0] == x; //如果有,要么等于x,要么<= x-arr[i]
for (int i=0;i<len;++i)
{
if (arr[i]==x||is_sum(arr+i+1,len-i-1,x-arr[i]))
return true;
}
return false;
}
// [10/10/2013 qingezha] 给出一个整型数组num[],对其中的每个元素,输出在它左侧且比它小的最近元素,
// 要求时间复杂度为O(n)。例如int num[]={2,4,1,3},2无左侧最近元素;4左侧最近的是2;1没有;3左侧最近的是1.(百度面试题)
//void print_each_min(int *arr,int len)
//{
//
//}