深入理解数据结构——链表、栈、队列

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/puliao4167/article/details/88848808

一、链表

(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;
}

参考 《数据结构与算法分析》、《算法导论》

猜你喜欢

转载自blog.csdn.net/puliao4167/article/details/88848808