原文引用自 http://www.yesky.com/100/1889600.shtml
什么是新的?
细读上面两张表格,你会发现和vector比较这里增加了两个函数。
1、c.push_front(elem) —— 在头部插入一个数据。
2、c.pop_front() —— 删除头部数据。
调用方法和c.push_back(elem)和c.pop_back()相同,这些将来会告诉我们对于deque> 会非常有用,deque可以在前后加入数据。>
缺少了什么?
同时你也会发现相对于vector> 缺少了两个函数,你将了解到deque> 不需要它们。
1、capacity()—— 返回vector当前的容量。
2、reserve() —— 给指定大小的vector> 分配空间。
也就是加了俩东西,删掉的平常也很少用,先不管。
加了头插和头删,如果有这种需求的可以deque,之前碰到mergeinterval之类的题时,很多都是中间删除,因此deque也还是不起作用,所以还是用linklist来做
这样借助vector又对deque熟练起来.另外vector的erase操作一定要及时获取iterator,因为删除掉一个元素后,如果还是正常的循环后移,会多移一个元素。
另外查阅cplusplus,list是基于双项链表实现的,所以支持高校插入删除,不支持高校随机存取。与vector的区别就理解为vector和list的区别了。
另外今天还稍微用了下STL的map,犹记得当年质量极差的代码算precision recall 的时候,fawks大神说为啥不用map,包括廖师兄大神的python也是map= = 这个特别适合算这种有重复的的东西,例如wordcount有大量的查找,简单说是每次新来的词都要查,因此查找效率很重要,map底层是红黑树实现,O(logn)的时间复杂度,因此已经不错了,先把更好的hash搁置一下。而且里面插入特别方便,直接访问key就行了,如果找得到的话,是对应的key的value赋值。其实也提供了insert,但是不方便,不如直接[] 就插入了。理解这个key value 一直挺别扭,好像很多都有,hash,mapreduced,是通过数据库的PK来理解的,然后通过key不能重复,value是另外的一个可以重复的属性。find操作找不到返回end() 的迭代器。赋上wordcount的代码:
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <map>
#include <string>
using namespace std;
int main()
{
ifstream fin("E:\\VisualStudioWorkspace\\LeetCode\\UniqueBinaryTree\\Debug\\01BagInput.txt");
if(fin==NULL) return -1;
string line,splitword;
map<int,int> wordcount;
while(getline(fin,line,'\n'))
{
istringstream istr(line);
while(istr>>splitword)
{
int splitnum=atoi(splitword.c_str());
if(wordcount.find(splitnum)!=wordcount.end())
wordcount[splitnum]++;
else
wordcount[splitnum]=1;
}
}
for(map<int,int>::iterator it=wordcount.begin();it!=wordcount.end();it++)
{
cout<<it->first<<" "<<it->second<<endl;
}
return 0;
}
由于红黑树的优良查找性能,插入性能,如果有n个词,时间复杂度应该是log1+...+logn-1(插入或者查找选一个,每次都是logn),之前算过上界是nlogn 所以O(nlogn) 比一般的n^2要好
后来突然发现hash_map 不是C++标准的,而我却可以用,才发现一直用的MS的lib,好像和STL 还是cplusplus的说明很像。。
附上map和hash_map的用法,发现hash居然几次测试比map慢,是因为hash函数不好,导致collision太多的原因么?
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <map>
#include <hash_map>
#include <string>
#include <ctime>
using namespace std;
void hashmap_wordcount()
{
ifstream fin("E:\\2013-03-08-NTUInternship\\MetaSLForHuman\\Data\\PositiveSLAllFeatures.txt");
if(fin==NULL) return ;
string line,splitword;
hash_map<string,int> wordcount;
while(getline(fin,line,'\n'))
{
istringstream istr(line);
while(istr>>splitword)
{
//int splitnum=atoi(splitword.c_str());
hash_map<string,int>::iterator it=wordcount.begin();
if((it=wordcount.find(splitword))!=wordcount.end())
//wordcount[splitword]++;
it->second++;
else
//wordcount[splitword]=1;
wordcount.insert(make_pair<string,int>(splitword,1));
}
}
/*
for(hash_map<string,int>::iterator it=wordcount.begin();it!=wordcount.end();it++)
{
cout<<it->first<<" "<<it->second<<endl;
}*/
}
void map_wordcount()
{
ifstream fin("E:\\2013-03-08-NTUInternship\\MetaSLForHuman\\Data\\PositiveSLAllFeatures.txt");
if(fin==NULL) return ;
string line,splitword;
map<string,int> wordcount;
while(getline(fin,line,'\n'))
{
istringstream istr(line);
while(istr>>splitword)
{
//int splitnum=atoi(splitword.c_str());
map<string,int>::iterator it=wordcount.begin();
if((it=wordcount.find(splitword))!=wordcount.end())
it->second++;
else
//wordcount[splitword]=1;
wordcount.insert(make_pair<string,int>(splitword,1));
}
}/*
for(map<string,int>::iterator it=wordcount.begin();it!=wordcount.end();it++)
{
cout<<it->first<<" "<<it->second<<endl;
}*/
}
int main()
{
clock_t start,finish;
start=clock();
map_wordcount();
finish=clock();
cout<<(double)(finish-start)/CLOCKS_PER_SEC<<endl;
start=clock();
hashmap_wordcount();
finish=clock();
cout<<(double)(finish-start)/CLOCKS_PER_SEC;
return 0;
}
借此总结一下代码实现算法数据结构问题的思路:
1. 想出尽可能多的解法,从简单的开始逐步优化,直至确定要实现的算法
2.设计框架,一块写一个注释模块,记录灵光一现的变量设计和处理
3.coding实现展开模块
4.判断输入数据各个边界条件,0 NULL 等
5.判断程序边界条件,数组,字符串访问上下越界,指针访问是否NULL,stack做pop()是否空,然后指针式从下到上逐个处理是否可能出现exception,要单独处理这些case或者可以统一起来
今天看到一道面试题,挺有意思:
递归函数最终会结束,那么这个函数一定(不定项)
1:使用了局部变量;
2:有一个分支不调用自身;
3:使用了全局变量或者使用了一个或多个参数;
2是对的,如果没有出口的,话就是无穷递归了,感觉3也是对的,因为递归要朝着小的方向推进,1不对,阶乘就可以推翻,可能有人觉得return n*f(n-1) 中n也是局部变量,那么直接return f(n-1),
虽然函数没有太大意义。但是答案说3居然是错的,说有异常终止,感觉好牵强啊。。。如果这样的话我不设置递归出口,然后一直自身调用,最后栈溢出了,也异常结束了。。。
另外看到变量根据作用域类型分类,局部自动变量用的最多,auto可省略,以致大家都忽视了局部自动变量的存在,而看到了静态局部变量,于是想到可以考虑和递归结合起来,因为时间全局,一个函数可访问,刚好符合递归特性,这样一个值可以一直保存下来,以后设计递归程序有疑惑的时候就采用这种机制试试。