1.表
下标从0开始,到n-1结束的序列,叫做大小为n的表。
STL中的表:
vector-索引快(),在非末尾的地方插入和删除慢。
list-双向链表,插入和删除快,索引慢。
这两个查找都慢。
方法:
对所有容器都适用的方法:
size() 返回元素个数
clear() 删除所有元素
empty() 判断是否为空
适用于vector和list的方法:
push_back(x) 在表末插入元素x
pop_back() 删除表末对象
back() 返回表末对象
front() 返回表头对象
只适用于list,不适用于vector的方法:
push_front(x) 在表头插入元素x
pop_front() 删除表头对象
只适用于vector,不适用于list的方法:
vector重载了[ ]运算符但list没有
capacity() 返回vector最多能装多少元素
reserve(x) 设置vector最多能装x个元素,但这些元素还未初始化
resize(x) 设置vector最多能装x个元素,且这些元素已经被默认构造函数初始化了
两者都适用,但需要使用迭代器的方法:
insert(iterator,x) 在iterator的 前面 插入x,返回插入的地方,list快于vector
erase(iterator) 删掉iterator指向的对象 ,返回删掉的后面,list快于vector,删完了iterator就不存在了
erase(iterator1,iterator2) 删掉[iterator1,iterator2)之间的对象
2.vector的实现
#pragma once
template
class Vector {
private:
int thesize; //容量
int thecapacity; //最大容量
Object *objects; //指针
public:
enum { SPARE_CAPACITY = 16 }; //容器的最大容量
explicit Vector(int initsize = 0) //构造函数
: thesize(initsize), thecapacity(initsize + SPARE_CAPACITY)
{
objects = new Object[thecapacity];
}
Vector(const Vector & rhs) : objects(NULL)//拷贝构造函数
{
operator = (rhs); //*this.operator = (rhs) 相当于*this = rhs
}
~Vector() {
delete[] objects;
}//析构函数,要delete掉new出来的objects
const Vector & operator = (const Vector & rhs) { //重载的赋值运算符
if (this != &rhs) { //自赋值检测
delete[] objects;
thesize = rhs.size();
thecapacity = rhs.thecapacity();
objects = new Object[capacity()];
for (int k = 0; k < size(); k++)
objects[k] = rhs.objects[k]; //在赋值之前要重新更改容量
}
return *this;
}
void resize(int newsize) { //重新设置容量
if (newsize > thecapacity)
reserve(newsize * 2 + 1);
thesize = newsize;
}
void reserve(int newcapacity) { //重新设置最大容量
if (newcapacity > thesize) return;
Object *oldarray = objects;
objects = new Object[newcapacity];
for (int k = 0; k < thesize; k++) {
objects[k] = oldarray[k];
}
thecapacity = newcapacity;
delete[] oldarray;
}
Object & operator[](int index) {//重载的[]运算符
return objects[index];
}
bool empty() const {//判断Vector是否为空
return size() == 0;
}
int size() const {//返回容量
return thesize;
}
int capacity() const {//返回最大容量
return thecapacity;
}
void push_back(const Object & x) {//添加末尾元素
if (thesize == thecapacity) reserve(2 * thecapacity + 1);
objects[thesize++] = x;
}
void pop_back() { //删除末尾元素
thesize--;
}
const Object & back() const { //返回末尾元素
return objects[thesize - 1];
}
//把指针命名成迭代器
typedef Object* iterator;
typedef const Object* const_iterator;
iterator begin() {//begin()迭代器
return &objects[0];
}
const_iterator begin() const {
return &objects[0];
}
iterator end() {//end()迭代器
return &objects[size()];
}
const_iterator end() const {
return &objects[size()];
}
};
3.链表的实现
这本书上的实现太复杂了,先用之前课件上的,以后有时间再补。
单向链表
#include
using namespace std;
struct Node {
int data;
Node *next;
};
Node* creat() {//创建链表并返回头节点
Node *head, *tail, *pnew;
//*tail:尾结点,tail->next:尾指针
//头节点是第0个结点
head = new Node;
head->next = NULL; //堵住最后
tail = head; //尾结点和头结点重合
while (1) {
pnew = new Node;
cin >> pnew->data;
if (pnew->data == -1) {
delete pnew;
break;
}
tail->next = pnew; //让尾指针指向新节点
tail = pnew; //让新节点变成尾结点
pnew->next = NULL; //堵住最后
//上述三行加起来实现了把一个新节点挂在原来尾结点的后面
}
return head;
}
void insert(Node *head, Node *pnew, int ith) {//把某个节点插入到第ith个结点的后面
Node *p = head;
for (int i = 0; i < ith && p!=NULL ; i++) p = p->next; //跑完以后p就到了第ith个结点处了
if (p == NULL)
{
cout << "Error!" << endl;
return;
}
pnew->next = p->next; //将p的后继节点(第ith+1个结点)设为pnew的后继节点
p->next = pnew;
//将pnew设为p的后继节点
//注意此处的顺序不能颠倒,否则后面的链表就都丢了
}
void del(Node *head, int ith) { //删除第ith个结点
if (ith == 0) { //不能删头节点
cout << "Error!" << endl;
return;
}
Node *p = head, *q;
for (int i = 1; i < ith && p->next != NULL; i++) p = p->next;
//注意此处是要让i=ith-1,来删掉p后面的那个结点,故判断应为p->next!=NULL
if (p->next == NULL) {
cout << "Error!" << endl;
return;
}
q = p->next; //让q指向要删的结点
p->next = q->next; //将要删的结点的后置结点设为p的后置结点
delete q;
}
void show(Node *head) { //输出整个链表
Node *p;
for (p = head->next; p != NULL; p = p->next) { //遍历链表,注意不要从头节点开始
cout << p->data << ' ';
}
cout << endl;
}
void clear(Node* head) { //销毁整个链表
Node *p = new Node;
while (head->next != NULL) {
p = head;
head = p->next;
delete p;
}
}
int main() {
Node *head = creat();
show(head);
//这里有个坑,如果要添加新结点的话必须用下面这种new的方法,从堆里分配*p的空间
//不能把一个Node结构的地址传给insert函数,否则del的时候会delete一个栈空间,从而报错
Node *p = new Node;
p->data = 1;
p->next = NULL;
insert(head, p, 2);
show(head);
del(head, 3);
show(head);
clear(head);
return 0;
}
在单向链表上稍作修改,又添加了逆序输出的函数。
#include
using namespace std;
struct Node {
int data;
Node *next;
Node *previous;
};
Node* creat() {//创建链表并返回头节点
Node *head, *tail, *pnew;
//*tail:尾结点,tail->next:尾指针
//头节点是第0个结点
head = new Node;
head->next = NULL; //堵住最后
head->previous = NULL;
tail = head; //尾结点和头结点重合
while (1) {
pnew = new Node;
cin >> pnew->data;
if (pnew->data == -1) {
delete pnew;
break;
}
pnew->previous = tail;
tail->next = pnew; //让尾指针指向新节点
tail = pnew; //让新节点变成尾结点
pnew->next = NULL; //堵住最后
//上述三行加起来实现了把一个新节点挂在原来尾结点的后面
}
return head;
}
void insert(Node *head, Node *pnew, int ith) {//把某个节点插入到第ith个结点的后面
Node *p = head;
for (int i = 0; i < ith && p != NULL; i++) p = p->next; //跑完以后p就到了第ith个结点处了
if (p == NULL)
{
cout << "Error!" << endl;
return;
}
pnew->previous = p;
pnew->next = p->next;
p->next->previous = pnew;
p->next = pnew;
//注意此处的顺序不能颠倒,否则后面的链表就都丢了
}
void del(Node *head, int ith) { //删除第ith个结点
if (ith == 0) { //不能删头节点
cout << "Error!" << endl;
return;
}
Node *p = head, *q;
for (int i = 1; i < ith && p->next != NULL; i++) p = p->next;
//注意此处是要让i=ith-1,来删掉p后面的那个结点,故判断应为p->next!=NULL
if (p->next == NULL) {
cout << "Error!" << endl;
return;
}
q = p->next; //让q指向要删的结点
p->next = q->next; //将要删的结点的后置结点设为p的后置结点
q->next->previous = p;
delete q;
}
void show(Node *head) { //输出整个链表
Node *p;
for (p = head->next; p != NULL; p = p->next) { //遍历链表,注意不要从头节点开始
cout << p->data << ' ';
}
cout << endl;
}
void revshow(Node *tail) {
Node *p;
for (p = tail; p->previous != NULL; p = p->previous) {
cout << p->data << ' ';
}
cout << endl;
}
void clear(Node* head) { //销毁整个链表
Node *p = new Node;
while (head->next != NULL) {
p = head;
head = p->next;
delete p;
}
}
int main() {
Node *head = creat();
show(head);
//这里有个坑,如果要添加新结点的话必须用下面这种new的方法,从堆里分配*p的空间
//不能把一个Node结构的地址传给insert函数,否则del的时候会delete一个栈空间,从而报错
Node *tail;
for (tail = head; tail->next != NULL; tail = tail->next);
revshow(tail);
Node *p = new Node;
p->data = 1;
p->next = NULL;
insert(head, p, 2);
revshow(tail);
del(head, 3);
revshow(tail);
clear(head);
return 0;
}
循环链表
就是让链表的尾指针指向头节点。
4.栈
插入和删除操作只能在表末进行的表,这里把表末叫做栈顶。
push(进栈),pop(出栈),后进先出。
栈可以由stack,vector或list实现。
应用:
判断括号有没有匹配:
‘(’后面一定紧跟着‘)’,[],{}也同理
栈可以用来保存后面的状态。
#include
#include
#include
#include
后缀表达式求值(整数)
碰到运算符就让栈顶的两个元素出栈做运算。
#include
#include
#include
#include
using namespace std;
string input;
stack cal;
int rnum;
int lnum;
int main() {
getline(cin, input);
int len = input.size();
for (int i = 0; i < len; i += 2) {
switch (input[i]) {
case '+':
rnum = cal.top();
cal.pop();
lnum = cal.top();
cal.pop();
cal.push(lnum + rnum);
break;
case'-':
rnum = cal.top();
cal.pop();
lnum = cal.top();
cal.pop();
cal.push(lnum - rnum);
break;
case'*':
rnum = cal.top();
cal.pop();
lnum = cal.top();
cal.pop();
cal.push(lnum * rnum);
break;
case'/':
rnum = cal.top();
cal.pop();
lnum = cal.top();
cal.pop();
cal.push(lnum / rnum);
break;
default:
cal.push(input[i] - '0');
}
}
cout << cal.top();
return 0;
}
前缀表达式求值(浮点数)
http://bailian.openjudge.cn/practice/2694/
先翻转成后缀表达式,注意减和除的顺序。
#include
#include
#include
#include
#include
#include
using namespace std;
stack cal;
string in;
string input[100000];
double rnum;
double lnum;
double temp;
int main() {
getline(cin, in);
stringstream ss;
ss << in;
int num = 0;
while (ss >> input[num++]);
reverse(input, input + num);
for (int i = 1; i < num; i++) {
switch (input[i][0]) {
case '+':
rnum = cal.top();
cal.pop();
lnum = cal.top();
cal.pop();
cal.push(rnum + lnum);
break;
case'-':
rnum = cal.top();
cal.pop();
lnum = cal.top();
cal.pop();
cal.push(rnum - lnum);
break;
case'*':
rnum = cal.top();
cal.pop();
lnum = cal.top();
cal.pop();
cal.push(rnum * lnum);
break;
case'/':
rnum = cal.top();
cal.pop();
lnum = cal.top();
cal.pop();
cal.push(rnum / lnum);
break;
default:
stringstream stemp;
stemp << input[i];
stemp >> temp;
cal.push(temp);
break;
}
}
cout << fixed << setprecision(6) << cal.top();
return 0;
}
中缀表达式转后缀表达式
比较和,前者的后缀表达式为a b c * +,后者为a b + c *,显然输出字母的顺序跟运算的优先级有关, ,低级运算符碰上高级运算符,不能马上输出;反之可以,因为高级肯定算完了。把中缀表达式读入:
如果遇到数字,立即输出;如果遇到运算符和左括号,从栈中输出并弹出元素直到栈顶元素的优先级比遇到的运算符低,把遇到的运算符压入栈中;如果遇到右括号,则将栈内元素输出并弹出,直到栈顶为左括号,把左括号弹出但不输出。最后,将栈里的所有元素全部弹出。
#include
#include
#include
#include
#include
中缀表达式求值:结合和即可
5.队列
先进先出的容器。
入队:在队尾插入一个元素,出队:在队头删除一个元素。
双端队列:队头和队尾都可以修改元素。
STL deque的方法:
比vector多了front(),pop_front(),push_front(x)。
重载了[]运算符。
应用:广度优先搜索
框架:
BFS()
{
初始化队列
while(队列不为空)
{
取队首结点扩展,并将扩展出的结点放入队尾
队首结点出队
必要时要记住每个结点的父结点(搞个结构体当队列的)
}
}
例题:洛谷P1443