C++ <七>
//-----------------不为有希望才坚持,只因坚持才有希望-------day_15_AM-------------------------------------
/*回顾: 栈和队列
栈: 先进后出,前插前取(链表)(数组)
队列:后插前取(链表)(数组)//用数组实现的时候要考虑满还是没满;
二叉树的查找:【 root, left, right】;根节点(创建)Node* root =NULL;
没有根节点的时候不能访问根节点中的数据; 可能会出现段错误;
大量利用递归(整棵树的问题变成某个分支的问题,考虑怎样结束)
【树的高度】: 比较左右子树的高度,取其较高的再加上1 空树的高度为0;
删除某个节点:找到 左根插入右子树 指针指向右子树;
选择排序:两层循环; 【递归实现的一般都可以用循环和栈来实现】
*/
/*冒泡排序:每次只能比较相邻的两个元素,如果顺序不合适就交换;
排好序的标志是一轮比较下来没有发生交换;【多轮排序】,只要发生过交换则需要再进行至少一轮比较;
*/
/*
#include<iostream>//【冒泡排序】适用于一组数据不是很混乱的情况下使用;
using namespace std;//只能交换相邻的两元素
typedef int T;
#include<ctime>
void sort(T *a,int n)
{
bool bSwap=false;
do{
bSwap=false;
for(int i=1;i<n;i++)
{
if(a[i]<a[i-1])
{
swap(a[i],a[i-1]);
bSwap=true;
}
}
n--;//每次交换后最后一个元素总是最大的不需要再参与排序
}while(bSwap);//大括号内定义的变量只能在大括号内部使用;
}
int main()
{
const long N=30240;
int a[N];
for(int i=0;i<N;i++)
a[i]=N-i;
time_t t1=time(NULL);
sort(a,N);
time_t t2=time(NULL);
for(int j=0;j<20;j++)
cout<<a[j]<<' ';
cout<<endl;
cout<<"time="<<t2-t1<<endl;
}*/
/*【插入排序:】
除了第一个元素对每个新元素:
1)找左边合适的插入位置;先复制一份保存;
2)把从那个位置开始的所有数据右移一个位置;
3)边元素放到插入位置;
#include<iostream>//【大量数据移动】导致其排序速度变慢;
using namespace std;
#include<ctime>
typedef int T;
void sort(T*a,int n)
{
T temp=0;
for(int i=1;i<n;i++)
{
int pos;
temp=a[i];
for(pos=i;pos>0/*&&a[pos-1]>temp;pos--)
if(a[pos-1]>temp)
a[pos]=a[pos-1];
a[pos]=temp;
}
}
int main()
{
const long N=30240;
int a[N];
for(int i=0;i<N;i++)
a[i]=N-i;
time_t t1=time(NULL);
sort(a,N);
time_t t2=time(NULL);
for(int j=0;j<20;j++)
cout<<a[j]<<' ';
cout<<endl;
cout<<"time="<<t2-t1<<endl;
}
*/
/*快速排序:(最快的排序方法) 选出一个分界值把数组分为两个部分,
递归调用知道各部分中不超过一个元素结束;
取第一个、中间那个、 最后一个、 取三者中中间大小的值最为分界
#include<iostream>
using namespace std;
#include<ctime>
typedef int T;
void sort(T* a,int n)
{
if(n<=1) return;//必须有递归结束条件;
swap(*a,a[n>>1]);
T *L=a+1;//从左边找比分界大的;
T* R=a+n-1;//从右边找比分界小的;
T V=*a;//分界
while(L<R){//1248945323456546
while(*L<V&&L<R) L++;
while(*R>=V&&R>a) R--;
if(L<R)
swap(*L,*R);
}
if(V>*R) swap(*a,*R);
sort(a,R-a);//左边组递归
sort(R+1,n-1-(R-a));//右边组递归
}
int main()
{
const long N=102400;
int a[N];
for(int i=0;i<N;i++)
a[i]=N-i;
time_t t1=time(NULL);
sort(a,N);
time_t t2=time(NULL);
for(int j=0;j<20;j++)
cout<<a[j]<<' ';
cout<<endl;
cout<<"time="<<t2-t1<<endl;
}
//四种排序算法
#include<iostream>
using namespace std;
#include<ctime>
typedef int T;
void Ssort(T* a,int n)
{
for(int i=0;i<n-1;i++)
{
int t=i;
for(int j=i+1;j<n;j++)
if(a[j]<a[t])
{
t=j;
}
swap(a[i],a[t]);
}
}
void Msort(T* a,int n)
{
bool bSwap=false;
do{
bSwap=false;
for(int i=0;i<n-1;i++)
{
if(a[i]>a[i+1])
{
swap(a[i],a[i+1]);
bSwap=true;
}
}
}while(bSwap);
}
void Isort(T* a,int n)
{
for(int i=1;i<n;i++)
{
T temp=a[i];
int pos;
for(pos=i;pos>0;pos--)
if(a[pos-1]>temp)
a[pos]=a[pos-1];
a[pos]=temp;
}
n--;
}
void Qsort(T *a,int n)
{
if(n<=1) return;
swap(a[0],a[n>>1]);
T *L=a+1;
T* R=a+n-1;
while(L<R)
{
while(*L<*a&&L<R) L++;
while(*R>=*a&&R>a) R--;
if(L<R) swap(*L,*R);
if(*R<*a) swap(*R,*a);
}
Qsort(a,R-a);
Qsort(R+1,n-1-(R-a));
}
int main()
{
const long N=10240;
int a[N];
for(int i=0;i<N;i++)
a[i]=N-i;
time_t t1=time(NULL);
Ssort(a,N);
//Msort(a,N);
//Isort(a,N);
//Qsort(a,N);
time_t t2=time(NULL);
for(int j=0;j<20;j++)
cout<<a[j]<<' ';
cout<<endl;
cout<<"time="<<t2-t1<<endl;
}
*/
/*【模板】:自定义、 标准模板库(STL);【C++标准程序库--华中科技大学出版社】
*/
/*
#include<iostream>
using namespace std;
template<typename T>//函数模板, 每次定义都要有属于自己的template<typename T>
//typedef int T;//给定模板让编译器去猜属于哪种类型的
void sort(T *a ,int n)
{
for(int i=0 ;i<n;i++)
for(int j=0;j<i;j++)
if(a[i]>a[j])
swap(a[i],a[j]);
}
template<typename T>
void disp(T * a,int n)
{
for(int i=0;i<n;i++)
cout<<a[i]<<' ';
cout<<endl;
}
int main()
{
double ad[5]={3.3,5.5,2.2,1.1,0.0};
int cd[5]={3,5,6,7,3};
char ac[4]={'x','s','a','d'};
sort(ad,5);
sort(cd,5);
sort<char>(ac,4);//不指定为char类型的话,编译器会自行去猜
disp(ad,5);
disp(cd,5);
disp(ac,4);
}
*/
//【函数模板】
//模板编程又称通用类型编程;
//泛型编程;
/*
#include<iostream>
using namespace std;
#include<string>
template<typename T>
//typedef int T;//给定模板让编译器去猜属于哪种类型的
void sort(T *a ,int n)
{
for(int i=0 ;i<n;i++)
for(int j=0;j<i;j++)
if(a[i]>a[j])
swap(a[i],a[j]);
}
template<typename T>
void disp(T * a,int n)
{
for(int i=0;i<n;i++)
cout<<a[i]<<' ';
cout<<endl;
}
void sort(char * a[],int n)//特殊的,
{
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(strcmp(a[j],a[i])<0)
swap(a[i],a[j]);
}
int main()
{
double ad[5]={3.3,5.5,2.2,1.1,0.0};
int cd[5]={3,5,6,7,3};
char*ac[4]={"xy","st","ab","cd"};//string ac[4]={"xy","st","ab","cd"};
//特殊的,可以特殊定义、实现模板的重载;
//【编译器会优先选择非模板函数;定义模板的时候声明和定义不要分开;】
sort(ad,5);
sort(cd,5);
sort(ac,4);
disp(ad,5);
disp(cd,5);
disp(ac,4);
}
*/
//取得数组中最大元素的下标;
/*
#include<iostream>
using namespace std;
template<typename T>//templata<class T>
int max(T *a,int n)//模板要将整个定义都放在头文件中;
{
int MAX=0;
for(int i=1;i<n;i++)
{
if(a[MAX]<a[i])
MAX=i;
}
return MAX;
}
int main()
{
int a[5]={3,4,7,1,2};
char b[5]={'a','d','t','z','f'};
cout<<max(a,5)<<endl;
cout<<max(b,5)<<endl;
}*/
//【类模板】 与函数模板类似; 在使用类模板时必须明确指定模板形参类型
// 把模板形参确定为具体类型的过程称为【模板的实例化】; Stack<int> si; Stack<doble> sd;
//模板名<类型实参>共同构成类名; void show(Stack<T>) 增加必要的限制;
//【 多个不确定类型模板】
/*
#include<iostream>
using namespace std;
#include<string>
template<typename T,typename U>
struct Pair{
T first;
U second;
Pair():first(T()),second(U()){}//初始化避免垃圾数据
Pair(const T& f,const U& s):first(f),second(s){}
};
template<typename T,typename U>
void show(const Pair<T,U>& p)
{
cout<<p.first<<','<<p.second<<endl;
}
template<typename T,typename U>
Pair<T,U> mkpair(T f,U s)
{
return Pair<T,U>(f,s);
}
int main()
{
Pair<int,double> p1;
Pair<int ,string> p2(100,"hello");
show(p1);
show(p2);
show(mkpair('c',5));
show(Pair<char,int>('c',5));//<char,int>显得有点多余,后面的实参已经指定了类型;
}*/
//STL C++标准模板库--类模板;
//容器(container) 类模板---一般都有一个内部类iterator(迭代器)【指针】
//函数模板(通用算法);
//class iterator{ //智能指针,类模板中一般不直接使用指针而是用迭代器代替
// T *p;
//public://模仿指针的运算。【perator* 、operator ->/ operator++ --、operator == !=】
// iterator();
// iterator(const iterator& i");
// iterator(T* q);
//};
//区间为半开半闭 [begain,end) end 所指元素不在区间中;
//【容器】: 序列式容器(vector【动态数组】,deque [双端队列],List[双向链表]),
//关联式容器(都是二叉查找树模板)(map不可重复【映射】、multimap可重复【多映射】、set【数据集】、multiset【多数据集】)
/*容器的共性:
区间是由两个迭代器决定的一个范围;
vector<int> v1;//构造函数
vector<int>v2(v1);//拷贝构造函数
vector<int> v3(begain,end)//区间构造函数
容器支持运算符:=,>,>=,<,<=,==,1=;
intsert(迭代器,要插入的元素)//复制一份;
erase();
size()//已有元素的大小
empty()//容器是否为空
max_size()//这中容器的最大容量
clear()//清除容器
swap(c2)//交换两个容器
使用时都需要包含相应的头文件, 头文件名和容器名一致
begin()/end()都是成员函数、 begin() 指向第一个元素、end()指向最后一元素之后
end()所指向的元素不属于这个容器;
#include<iostream>
using namespace std;
#include<list>
int main()
{
list<int> l1;
int a[5]={3,4,5,6,7};
list<int> l2(a,a+5);
cout<<"l1.size():"<<l1.size()<<endl;
cout<<"l2.size():"<<l2.size()<<endl;
list<int>::iterator it;
for(it=l2.begin();it!=l2.end();it++)
cout<<*it<<' ';
cout<<endl;
it=l2.begin();
it++;
l2.erase(it);
l2.insert(l2.begin(),100);
l2.insert(l2.end(),200);
for(it=l2.begin();it!=l2.end();it++)
cout<<*it<<' ';
cout<<endl;
}
*/
//作业:递归选择排序, 找最大的元素与最后一元素交换递归 注意递归条件
/*
#include<iostream>
using namespace std;
template<typename T>
void Dssort(T* a,int n)
{
if(n<=1) return;
int max=0;
for(int i=1;i<n;i++)
if(a[max]<a[i])
max=i;
swap(a[max],a[n-1]);
n--;
Dssort(a,n);
}
template<typename Q>
void disp(Q *a,int n)
{
for(int i=0;i<n;i++)
cout<<a[i]<<' ';
cout<<endl;
}
int main()
{
int a[10]={1,3,4,6,2,5,7,8,0,11};
char b[10]={'l','k','j','h','g','f','d','s','a','t'};
Dssort(a,10);
Dssort(b,10);
disp(a,10);
disp(b,10);
}
*/
//--------------踏实------day_16_AM-------------------------------
/*回顾:
排序算法:注意逻辑的严密性;不要出现野指针, 【Node* p=NULL;】
选择排序、冒泡排序、插入排序、快速排序;
模板: 声明和定义不要分开; 自定义模板( tempplate<typename T> 类模板<类型> 标准模板库STL;
容器: 都支持构造【 构造函数、拷贝构造函数、区间构造函数】
指定位置插入、删除、比较、 size()、empty()、clear()
【 反向迭代器 reverse_iterator】: rbegin()/rend()
【const_iterator】
【const_reverse_interator】
容器适配器:stack、queue、priority_queue(优先队列、操作和栈一样,头文件使用的是队列一样)
容器: 序列式容器【sequence】--vector、list、deque,
共性:
构造函数 constructor(int n),constructor(int n,T val) 、
调整大小 resize(int n)/ resize(int n,T val);
赋值 assign(n,val): 清除原来的数据、在容器中放n个val的数据 assign(区间)
插入 insert(pos,n,val)/insert(pos,区间)【pos为iterator】/
末未插入新数据:push_back(val)
首尾元素: front()/back()
尾删除:pop_back();
//有了序列容器可以不再使用动态内存
序列容器的特性:
vector 类似于动态数组但更加安全,能自动增长;
可以检查下表是否越界 at(下标),只适合在末尾插入删除数据,
在头部会有大量的数据移动
reserve(int n) 保留容量
capacity() 取得当前的容量;
deque 是vector的子类,可以从头尾操作;deque相比vector多了 push_front(val) pop_front();
list 可以在任何位置插入删除, 查找的效率较低;
支持 push_front push_back() pop_back() pop_front();
不支持[]/at 因为它的数据在内存中不是连续存放的;
【sort()需重载<】; 【unique() 需重载 ==】
splice 转移 c1.splice(c2) :c2中的数据转移到c1 转以后c2中不在有元素
c1.splice(pos,c2)
c1.splice(pos,c2,it);把c2中it位置的数据转移到c1中的pos位置,其中pos,it都为迭代器
c1.splice(pos,c2,区间) 把c2中该区间中的数据转移到c1的pos位置
c1.merge(c2) 把c2中的元素归并到c1的合适位置【归并排序】 归并后c2为空链表
c1.remove(val)//删除指定元素
*/
/*
#include<iostream>
using namespace std;
#include<vector>//可以替代new操作,免了delete操作
void show(vector<int> vi)
{
vector<int>::iterator it;
for(it=vi.begin();it!=vi.end();it++)
{
cout<<*it<<' ';
}
cout<<endl;
}
int main()
{
vector<int> vi(3,90);
show(vi);
int a[5]={3,4,5,6,7};
vi.insert(vi.begin(),a,a+5);
show(vi);
vi.push_back(100);
show(vi);
cout<<"size:"<<vi.size()<<endl;
vi.assign(5,99);
show(vi);
cout<<"size:"<<vi.size()<<endl;
}
#include<iostream>
using namespace std;
#include<vector>
int main()
{
vector<int> vi;
cout<<"input scores, end by -1:";
int s;
int m=0;
for(;;){
cin>>s;
if(s==-1) break;
vi.push_back(s);
if(s>m)
m=s;
}
int inc=100-m;
for(int i=0;i<vi.size();i++)
{
vi[i]+=inc;
}
for(int j=0;j<vi.size();j++)
{
cout<<vi[j]<<' ';
}
cout<<endl;
try{
cout<<"vi[1000]="<<vi[1000]<<endl;//尽量使用[],避免使用at(),自行设置下标
cout<<"vi.at(1000)="<<vi.at(1000)<<endl;//at检测下标是否越界,会减低性能
}catch(exception &e){//标准库中的异常都可以用他来catch;
cout<<e.what()<<endl;
}
}
*/
/*
#include<iostream>
using namespace std;
#include<list>
int main()
{
int cpp[5]={3,6,1,7,5};
int java [8]={6,4,7,8,15,2,3,9};
int unix[4]={5,2,6,9};
list<int> li;
li.insert(li.begin(),cpp,cpp+5);
li.insert(li.begin(),java,java+5);
li.insert(li.begin(),unix,unix+5);
li.sort();
li.unique();//相邻两元素不相同;
list<int>::iterator it=li.begin();
while(it!=li.end())
{
cout<<*it++<<' ';
}
cout<<endl;
}
*/
/*【关联式容器】:数据之间存在关系;
共性:
insert(val);不需要指定位置, 只要给数据就可以;会自动找到合适的位置,排好序;
find(val);查找一个指定的数据;返回指向找到元素的迭代器;没找到返回end() ,并非NULL
multimap/multiset://允许有重复元素
count(val);统计指定值的个数:
lower_bound(val) 指定值的第一个位置
upper_bound(val) 指定值最后一个后面一个位置
[lower_bound(val),upper_lound),,为指定值的区间;
equal_range(val);//返回迭代器; Pair(迭代器1,迭代器2),迭代器1==lower_lound(val);迭代器2==upper_lound(val);
特性:
map: 可重复, Pair<key,value>; key->value (key唯一,可做索引找到value,支持"[key]"方括号操作)
multimap: 不重复, Pair<key,value>
set: 可重复, key
multiset: 不重复, key
//不可重复指的是key不允许重复;
#include<iostream>
using namespace std;
#include<map>
#include<string>
int main()
{
map<int,string> mis;
mis.insert(make_pair(63,"李鹏"));
// mis.insert(make_pair(32,"王媛"));
//mis.insert(make_pair(36,"李鹏"));
mis[20]="覃丹";
//mis.insert(make_pair(32,"娜娜"));//重复时不再插入;
// map<int,string>::iterator it;
// it=mis.begin();
// while(it!=mis.end()){
// cout<<(it->first)<<":"<<(it->second)<<endl;
// ++it;
// }
}
#include<iostream>
using namespace std;
#include<map>
#include<string>
int main()
{
typedef multimap<string,string> Mul;
Mul mss;
Mul::iterator ib,ie;
mss.insert(make_pair("aa","aa"));
mss.insert(make_pair("cc","dd"));
mss.insert(make_pair("ee","ff"));
mss.insert(make_pair("cc","pp"));
mss.insert(make_pair("aa","mm"));
mss.insert(make_pair("cc","dd"));
mss.insert(make_pair("aa","kk"));
ib==mss.begin();
ie=mss.end();
while(ib!=ie){
cout<<ib->first<<"的好友"<<ib->second<<endl;
++ib;
}
cout<<"aa的好有个数:"<<mss.count("aa")<<endl;
ib=mss.lower_bound("cc");
ie=mss.upper_bound("cc");
cout<<"cc的好友:"<<endl;
while(ib!=ie){
cout<<ib->second<<endl
}//都是按key来排序的,与value无关系, 所以必须支持<操作符;
}
*/
/*
set:
multiset:
#include<iostream>
using namespace std;
#include<set>//用set 设置用户权限
int main()
{
//set<int> si;
int userid[5]={3,8,6,4,3};//重复的插入被忽略
//for(int i=0;i<5;i++)
// si.insert(userid[i]);
set<int> si(userid,userid+5);
set<int>::iterator it;
it=si.begin();
while(it!=si.end())
cout<<*it++<<' ';
cout<<endl;
cout<<"user 3:"<<(si.find(3)!=si.end())<<endl;
cout<<"user 5:"<<(si.find(5)!=si.end())<<endl;
}
算法: for_each()对每个元素调用指定的函数,for_each(beg,end,函数名)
//find(begin,end,数据)算法一般都工作在区间内;
头文件<algorithm>
find_first_of(),find_end()
sort(),reverse()
copy() copy_backward()
replace(),merge(),
remove();//假删除, 需使用容器的erase才能真正删除;
头文件<numeric>
min(),cout(),swap(),accumulate()
*/
/*
复习:
基本语法: 流程控制, 函数(static可延长生命期),
递归(每次执行完都回到调用它的位置,一定要有结束条件,否则会出现段错误),
引用:初始化引用时要用长久不改变的变量; const T& ,直接使用旧变量
指针:指针不是最重要的;指针只能保存地址,地址也只能用指针来保存; 指针与*指针区别;
函数名就是函数的地址, 向定义函数一样第一函数指针指示将函数名改成(*指针) new 类型 [个数] delete [] p;
动态内存的使用;可以利用vector来代替动态数组;
oo面向对象编程:
定义类:创建对象是自动调用相对应的构造函数, 拷贝构造函数,
默认拷贝构造函数(按字节复制) 如果存在继承关系, 默认调用父类的无参构造函数,
使用对象: 对象.成员函数; 继承: 子类继承父类子类的全部成员, 也可自行定义覆盖父类的
多态: 统一管理,以父类身份统一管理子类;虚函数, 通过指针或者引用
扩展:
运算符的重载: ,友元形式; operator ¥ (-,-)
成员形式 : 默认使用当前对象, 只需传递剩余参数;
前++,-- 后++,--; 多传递一个int形参;
<< >> istream & operator >>(istream& s,T & o)
ostream & operator <<(ostream& s,T & o)
类型转换: operator int() 不需要写返回类型,
静态: 共用一份, 属于整一个类,想定义全局变量一样定义;
IO: 文件对象的创建; istream fout("文件名"), 文件操作与控制台的操作一样;
异常: try { }catch{}
模板: template<typename T>; 函数模板编译器主动识别, 类模板需要主动实例化
类模板->容器, 函数模板-> 算法 , 通过迭代器iterator将容器和算法联系起来;
数据结构: 链表【注意段错误】, 栈【前插前去LIFO】,队列【FIFO】, 二叉树
算法: 选择排序,冒泡排序,插入排序,快速排序
无参的函数一般不能递归;