一、链表
(1)数组和链表的比较
- 数组是在连续的地址空间中存储,链表可以不连续
- 数组的查找较为方便,然而插入和删除操作花销大,需要整块数组的移动
- 链表的查找不方便,需从表头开始遍历,但是插入和删除操作较为方便
- 数组在初始化时候需要给定大小,链表则不用
(2)单链表的实现
在链表中利用指针实现较为简单,数据存储在一个结构体中,每个结构体包含数据以及指向下一个结构体的指针。当一个新的链表节点想要插入链表中,可以通过调用malloc/new从堆中动态分配内存,最后通过free/delete释放。
/*
* @Description: 单向链表头文件
* @Author: haha_giraffe
* @Date: 2019-03-16 13:06:22
*/
#ifndef LINKLIST_H
#define LINKLIST_H
#include "../head.h"
typedef struct Node* List;
typedef Node* Position;
struct Node
{
int val;
Node *next;
Node (int v):val(v),next(NULL){ }
};
class Linklist
{
public:
List head;
public:
~Linklist();
Linklist(vector<int> vec);
List MakeEmpty();
int isEmpyt();
int isLast(Position p);
Position Find(int x);
void Delete(Position p);
Position FindPrevious(int x);
void Insert(int x,Position p);
void DeleteList();
void PrintList();
//Position Head(List l);
//Position First(List l);
//Position Advance(Position p);
//int Retrieve(Position p);
};
#endif
/*
* @Description: 单向链表实现与测试
* @Author: haha_giraffe
* @Date: 2019-03-16 13:19:00
*/
#include "Linklist.h"
Linklist::~Linklist(){
DeleteList();
}
//如果链表为空则返回true
int Linklist::isEmpyt(){
List l=head;
return l->next==NULL;
}
//如果Position为链表最后一个结点则返回true
int Linklist::isLast(Position p){
return p->next==NULL;
}
//链表查找,如果找到则返回第一个值所对应的位置,否则为空
Position Linklist::Find(int x){
List tmp=head;
while(tmp!=NULL){
if(x==tmp->val){
break;
}
tmp=tmp->next;
}
return tmp;
}
//在链表l中,插入结点x到p所指向的位置的后方
void Linklist::Insert(int x,Position p){
Node *newnode=new Node(x);
newnode->next=p->next;
p->next=newnode;
}
//找到值为x的前一个位置
Position Linklist::FindPrevious(int x){
List tmp=head;
while(tmp->next!=NULL && tmp->next->val != x){
tmp=tmp->next;
}
return tmp;
}
//删除x结点
void Linklist::Delete(Position p){
List l=head;
while(l->next!=p){
l=l->next;
}
if(p->next==NULL){
l->next=NULL;
delete p;
}
else{
l->next=p->next;
delete p;
}
}
//删除整个链表
void Linklist::DeleteList(){
List tmp=head->next;
head->next=NULL;
List tmp2;
while(tmp!=NULL){
tmp2=tmp->next;
delete tmp;//注意delete以后就不能用tmp->next,因为其没有指向任何一个对象
tmp=tmp2;
}
}
//把整个链表置空
List Linklist::MakeEmpty(){
List l=head;
List tmp=l->next;
while(tmp){
tmp->val=0;
tmp=tmp->next;
}
return l;
}
//构造函数
Linklist::Linklist(vector<int> vec){
List l=new Node(0);
List tmp=l;
for(int i=0;i<vec.size();i++){
List newnode=new Node(vec[i]);
tmp->next=newnode;
tmp=tmp->next;
}
tmp->next=NULL;
head=l;
}
//打印链表
void Linklist::PrintList(){
List l=head;
while(l){
cout<<l->val<<" ";
l=l->next;
}
cout<<endl;
}
int main(){
vector<int> vec{2,7,4,9,6};
Linklist *ll=new Linklist(vec);
// cout<<ll->head->val<<" "<<ll->head->next->val;
// ll->PrintList();
// ll->Delete(ll->head->next->next);
// ll->PrintList();
// cout<<ll->Find(9)->val;
// ll->Insert(100,ll->head->next->next);
// ll->PrintList();
delete ll;
return 0;
}
二、栈
栈模型的特点是“先进后出”,有两种方法实现栈,一种是使用指针,另一种是使用数组,两种方法都比较简单,这里以链表指针为例。栈的应用比较广:
- 平衡符号,编译器可以利用栈实现括号匹配
- 计算式求值,可以用一个栈+后缀表达式计算(也可以用两个栈+中缀表达式计算)
- 函数调用,在进程的虚拟地址空间中,可以利用栈来对函数调用中存储重要信息,比如:返回地址,参数,寄存器值等
/*
* @Description: 用链表表示栈
* @Author: haha_giraffe
* @Date: 2019-03-16 20:19:30
*/
#ifndef STACK_LIST_H
#define STACK_LIST_H
#include "../head.h"
typedef struct Node* ptrnode;
struct Node {
int val;
Node *next;
Node():val(0),next(NULL){ }
Node(int x):val(x),next(NULL){ }
};
class Stack{
public:
int IsEmpty();
void CreateStack();
void MakeEmpty();
void Push(int x);
void Pop();
int Top();
void Print();
public:
ptrnode head;//头结点
};
#endif
/*
* @Description: 用链表表示栈
* @Author: haha_giraffe
* @Date: 2019-03-16 20:18:56
*/
#include "stack_list.h"
void Stack::CreateStack(){
ptrnode tmp=new Node();//建立一个头结点
if(tmp==NULL){
printf("error create\n");
}
tmp->next=NULL;
head=tmp;
return ;
}
int Stack::IsEmpty(){
return head->next==NULL;
}
void Stack::MakeEmpty(){
if(head==NULL){
printf("error\n");
}
else{
while(!IsEmpty()){
Pop();
}
}
}
void Stack::Push(int x){
ptrnode tmp=new Node(x);
if(tmp==NULL){
printf("error new\n");
}
else{
tmp->next=head->next;
head->next=tmp;
}
}
void Stack::Pop(){
if(!IsEmpty()){
ptrnode temp=head->next;
head->next=head->next->next;
delete(temp);
}
else{
return;
}
}
int Stack::Top(){
if(!IsEmpty()){
return head->next->val;
}
else{
printf("top error\n");
return 0;
}
}
void Stack::Print(){
ptrnode temp=head->next;
while(temp){
printf("%d ",temp->val);
temp=temp->next;
}
printf("\n");
}
int main(){
Stack s;
s.CreateStack();
for(int i=0;i<10;i++){
s.Push(i);
}
s.Print();
s.Pop();
s.Print();
printf("%d\n",s.Top());
s.MakeEmpty();
s.Print();
return 0;
}
三、队列
队列模型特征为“先入先出”,最方便的方法是由循环数组实现队列,重点在于双指针front和rear的作用,以及如何判断队列为空。队列的应用也有很多,进程调度、作业调度都有用到队列。
/*
* @Description: 队列的数组实现
* @Author: haha_giraffe
* @Date: 2019-03-18 10:36:47
*/
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include "../head.h"
typedef struct Queue_data* Queue_data_ptr;
struct Queue_data
{
int capacity;
int front;
int rear;
int size;
int array[];
};
class Queue
{
public:
int IsEmpty();
int IsFull();
void Enqueue(int x);
void Dequeue();
int Front();
void CreateQueue();
void MakeEmpty();
void Print();
private:
Queue_data_ptr q;
};
#endif
/*
* @Description: 队列的数组实现
* @Author: haha_giraffe
* @Date: 2019-03-18 11:51:33
*/
#include "queue.h"
void Queue::CreateQueue(){
q->front=1;
q->rear=0;
q->size=0;
q->capacity=10;
q->array[q->capacity]={0};
}
int Queue::IsEmpty(){
return q->size==0;
}
int Queue::IsFull(){
return (q->size==q->capacity);
}
void Queue::Enqueue(int x){
if(IsFull()){
printf("full\n");
}
else{
q->size++;
q->rear=(q->rear+1)%(q->capacity);
q->array[q->rear]=x;
}
}
void Queue::Dequeue(){
if(IsEmpty()){
printf("empty\n");
}
else{
q->size--;
q->array[q->front]=0;
q->front=(q->front+1)%(q->capacity);
}
}
int Queue::Front(){
return q->array[q->front];
}
void Queue::MakeEmpty(){
q->size=0;
while(q->rear!=q->front){
q->array[q->front]=0;
q->front=(q->front+1)%q->capacity;
}
q->array[q->front]=0;
}
void Queue::Print(){//打印方向相反
int tmp=q->front;
while(q->rear!=tmp){
cout<<q->array[tmp]<<" ";
tmp=(tmp+1)%q->capacity;
}
cout<<q->array[tmp]<<endl;
}
int main(){
Queue queue;
queue.CreateQueue();
queue.Enqueue(7);
for(int i=0;i<7;i++){
queue.Enqueue(i);
}
queue.Print();
queue.Dequeue();
queue.Dequeue();
queue.Print();
cout<<queue.Front();
return 0;
}
参考 《数据结构与算法分析》、《算法导论》