|
Andy Niu Help
1.0.0.0
|
模块 | |
| 随机数 | |
变量 | |
| 一亿整数排序 | |
| 全排列 | |
| 变位词 | |
| 循环移位 | |
| 过滤重复字符并排序 | |
详细描述
变量说明
| 一亿整数排序 |
1、考虑下面的需求,对文件中的1亿个32位整数排序,这些整数不重复。
使用new int[1亿],从文件中读取所有的整数,放入数组,排序。这种方法不可以行,因为需要的内存太大。
怎么解决这个问题?
2、使用位向量表示整数,也就是第n个bit,表示整数n,bit的取值为整数出现的次数,0没有出现,1出现。
而上面的方法,整数需要4个字节,32bit表示。因此,位向量只用到原来的32分之1。
注意:如果重复次数不超过3,使用两个bit表示一个整数。也就是第2n-1和第2n个bit表示整数n,
这两个bit的取值表示出现次数。
3、为了简化问题,考虑30个整数,使用位向量排序。
new出32bit的内存,也就是4个字节。从文件中遍历读取整数,在对应的第n个bit,把bit置为1
遍历位向量,输出bit值为1的位置。
4、位向量也就是,第n个bit表示整数n,bit的取值为整数n出现的次数。
5、考虑,如果文件中的数字更多,比如说有40亿,怎么处理?
多趟处理,第一趟遍历40亿整数,过滤出大于等于0,小于1亿[0,1亿),排序输出到目标文件,
第二趟遍历40亿整数,过滤出大于等于1亿,小于2亿[1亿,2亿),排序输出到目标文件。
头文件
#ifndef PEARLS_H_
#define PEARLS_H_
typedef unsigned char uint_8;
namespace Pearls
{
void SetBitValue(uint_8* start,int index,int value);
int GetBitValue(uint_8* start,int index);
}
#endif
源代码
#include "pearls.h"
namespace Pearls
{
void SetBitValue(uint_8* start,int index,int value)
{
int bytePos = index/8; // 第几个字节
int bitPos = index%8; // 字节内的第几个bit
start+=bytePos;
if(value == 0)
{
(*start)&=(~(1<<bitPos)); // 把某一个bit置成0,与上一个对应位是0,其它位是1的数,这里有个取反
}
else if(value == 1)
{
(*start)|=(1<<bitPos); // 把某一个bit置成1,或上一个对应位是1,其它位是0的数
}
}
int GetBitValue(uint_8* start,int index)
{
int bytePos = index/8; // 第几个字节
int bitPos = index%8; // 字节内的第几个bit
// 做个副本,转化为unsigned,再移位
uint_8 ch = *(start+=bytePos);
return ((ch)&=(1<<bitPos))>>bitPos;
}
}
测试
#include "pearls.h"
#include <stdio.h>
#include <string.h>
#include <fstream>
void ReadFileAndSetBitVec(uint_8* bitVec)
{
FILE* pf = fopen("./ints.txt","r");
char readBuf[4] = {0};
char* pBuf = readBuf;
while(feof(pf) == false)
{
if((*pBuf++ = fgetc(pf)) == ' ')
{
int value = atoi(readBuf);
Pearls::SetBitValue(bitVec,value,1);
memset(readBuf,0,4);
pBuf = readBuf;
}
}
}
void PrintBitVec(uint_8* bitVec)
{
for(int i = 0; i<32; ++i)
{
int dd = Pearls::GetBitValue(bitVec,i);
if(Pearls::GetBitValue(bitVec,i) == 1)
{
printf("%d ",i);
}
}
}
int main(int argc, char* argv[])
{
uint_8* bitVec = new uint_8[4];
memset(bitVec,0,4);
ReadFileAndSetBitVec(bitVec);
PrintBitVec(bitVec);
getchar();
return 0;
}
| 全排列 |
1、考虑字符串1234,输出全排列,如下:1234,1243,1324,1342,1423,1432,.....
2、思路一,初始左边为"",右边为1234,递归结束条件是右边只有一个字符,输出左边,和右边的一个字符。
3、思路二,全排列,最小是1234,最大是4321,从1234开始,找出下一个比1234大,且差值最小的排列,
一直找到4321结束。
void PrintTotalOrder(const string& str)
{
//PrintOrder("",str);
PrintOrder_2(str);
}
void PrintOrder(const string& prefix,const string& other)
{
if (other.size()==1)
{
//如果后缀只剩一个字符,输出当前顺序,递归结束
printf("%s%s\n",prefix.c_str(),other.c_str());
}
else
{
//如果后缀长度大于1,每次把后缀中的一个字符添加到前缀中,递归
for (int i=0;i<other.size();i++)
{
PrintOrder(prefix+other.substr(i,1),string(other).erase(i,1));
}
}
}
void PrintOrder_2(const string& str)
{
string cpy = str;
do
{
printf("%s\n",cpy.c_str());
}while(Next(cpy));
}
bool Next(string& str)
{
int len = str.size();
// 从后向前,找到第一个位置,这个位置的元素小于下一个位置的元素
int pos = len-2; // 假定是倒数第二个元素
while(pos>=0 && str[pos]>str[pos+1]) // 不满足条件,向前移动
{
--pos;
}
if(pos==-1) // 位置不存在,已经是逆序排列,取值最大
{
return false;
}
else
{
// 从后面找出一个比当前下标大的元,这个backpos必定存在。
int backPos = len-1;
while(backPos>0 && str[backPos] < str[pos])
{
--backPos;
}
Swap(str[pos],str[backPos]);
ReverseString(str,pos+1,str.size());
return true;
}
}
// 对string的left和right区间反转
void ReverseString(string& str,int left,int right)
{
if(left>right)
{
Swap(left,right);
}
if(right>str.size())
{
return;
}
int mid = (left+right)/2;
for(int i = left;i<mid;++i)
{
Swap(str[i],str[--right]); // left下标加1,right下标减1
}
}
int main(int argc, char* argv[])
{
Pearls::PrintTotalOrder("1234");
getchar();
return 0;
}
| 变位词 |
1、考虑下面的需求,给出包含单词的文档,找出其中的变位词。所谓的变位词,比如dog,god互为变位词。
2、最笨的办法是,dog不动,对god,cat进行排列组合,看看是否存在于dog相同的情况,存在就是变位词。
3、上面的办法效率太差,很长的单词,排列组合很多,cpu开销太大。怎么解决?
4、变位词的特点是,大家所包含的字符相同,只不过排列组合不同。包含的字符,可以认为是单词的标识符,
标识符按字符顺序排列好,互为变位词,也就是说标识符相同。因此,解决思路是,对每个单词求标识符,
比较标识符,相同就是变位词。
5、使用map,key是标识符(可能是一个无效的单词),value是vector,保存互为变位词的单词集合。
6、更广泛的用处,比如使用搜狗输入法输入 ty,就会提示 统一,同意,同义等等。这种情况也类似,对
统一,同意,同义求标识符,放入map的key,value保存这些词。
string GetIdentifier(const string& word)
{
char buf[64] = {0};
sprintf(buf,word.c_str());
qsort(buf,strlen(buf),sizeof(char),CharCmp);
return buf;
}
int CharCmp(const void* a,const void* b)
{
return *(char*)a - *(char*)b;
}
bool IsSeparator(char ch)
{
return (ch == ' ' || ch == ',' || ch == '.'|| ch == '\r'|| ch == '\n');
}
void GetWord(vector<string>& wordVec,char* buf)
{
buf[strlen(buf)-1] = '\0'; //去除分隔符
if(strlen(buf) > 0)
{
wordVec.push_back(buf);
}
}
void ReadWordFromFile(vector<string>& wordVec)
{
FILE* pf = fopen("./words.txt","r");
char readBuf[64] = {0};
char* pBuf = readBuf;
while(feof(pf) == false)
{
*pBuf = fgetc(pf);
if(IsSeparator(*pBuf))
{
GetWord(wordVec,readBuf);
memset(readBuf,0,64);
pBuf = readBuf;
}
else
{
++pBuf;
}
}
}int main(int argc, char* argv[])
{
vector<string> wordVec;
Pearls::ReadWordFromFile(wordVec);
map<string,vector<string> > wordMap;
for(vector<string>::iterator iter = wordVec.begin();
iter != wordVec.end();++iter)
{
wordMap[Pearls::GetIdentifier(*iter)].push_back(*iter);
}
getchar();
return 0;
}
| 循环移位 |
1、循环移位,对于abcdefgh,向左循环移动3位,成为defghabc,给出一个字符数组和需要移动的位数n,输出结果。
2、一场考虑,字符指针为NULL,对于8个字符,左移11位等价于左移3位。
3、方法一:从头部取出一个字符,后面的依次左移,取出的字符放入尾部,执行n次。
4、方法一的cpu开销大,方法二是:分配n个字节的缓冲区,从头部取出n个字节,后面的字符左移n位,取出的n字节
字符放入尾部。
5、方法二的内存开销大,需要多分配n字节字符。方法三,利用循环左移的特点,循环左移3位等价于前面3个反转,
后面的字符反转,然后整个字符串反转。如下:abcdefgh-->cbadefgh-->cbahgfed-->defghabc
void LoopMove(char* pc,int moveSize)
{
if(pc == NULL)
{
return;
}
int len = strlen(pc);
if(len <= 1)
{
return;
}
moveSize = moveSize%len;
// 每次左移一位,循环n次
//for(int i=0;i<moveSize;++i)
//{
// char first = pc[0];
// for(int j=0;j<len-1;++j)
// {
// pc[j]=pc[j+1];
// }
// pc[len-1]=first;
//}
// 一次左移n位
//char* buf = new char[moveSize];
//memset(buf,0,moveSize);
//memcpy(buf,pc,moveSize);
//for(int j=0;j<len-moveSize;++j)
//{
// pc[j]=pc[j+moveSize];
//}
//memcpy(pc+len-moveSize,buf,moveSize);
// 反转三次
Reverse(pc,pc+moveSize);
Reverse(pc+moveSize,pc+len);
Reverse(pc,pc+len);
}
void Reverse(char* first,char* last)
{
int len = last-first;
for(int i=0;i<len/2;++i)
{
char tmp = first[i];
first[i] = first[len-i-1];
first[len-i-1]=tmp;
}
}
{
char buf[] = "abcdefgh";
Pearls::LoopMove(buf,3);
Pearls::LoopMove(buf,3);
Pearls::LoopMove(buf,4);
Pearls::LoopMove(buf,81);
getchar();
return 0;
}
| 过滤重复字符并排序 |
void GetCharCountByMap(const string& str)
{
map<char,int> chMap;
for(int i = 0;i<str.size();++i)
{
chMap[str[i]]++;
}
for(map<char,int>::iterator iter = chMap.begin();
iter != chMap.end(); ++iter)
{
printf("[%c:%d]\n",iter->first,iter->second);
}
}
void GetCharCountByHashTable(const string& str)
{
// 最多512个字符,下标对应字符,下标取值对应出现的次数
int hashTable[512]={0};
for(int i = 0;i<str.size();++i)
{
hashTable[str[i]]++;
}
for(int i=0; i<512; ++i)
{
if(hashTable[i]>0) // 出现过
{
printf("[%c:%d]\n",i,hashTable[i]);
}
}
}
int main(int argc, char*argv[])
{
string str = "acbbdkdehfghgefahb";
Pearls::GetCharCountByMap(str);
Pearls::GetCharCountByHashTable(str);
return 0;
}
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.