Bootstrap

数据结构探险(三)—— 线性表

  • 线性表是n个数据元素的有限序列
  • 线性表分为:
  1. 顺序表(数组):特点是访问速度快,搜索能力强
  2. 链表:静态链表,单链表,循环链表,双向链表
  • 应用场景:通讯录;一元多项式;

线性表


c语言表示:
在这里插入图片描述

#ifndef LIST_H
#define LIST_H
typedef int Elem;
class List
{
public:
    List(int size); //构造函数
    ~List(); //析构函数
    void ClearList();
    bool ListEmpty();//在c中没有bool类型,需要用宏定义定义BOOL
    int ListLength();
    bool GetElem(int i,Elem *e);//将下标为i的元素用e指针所指向的内存获取
    int LocateElem(Elem *e);
    bool PriorElem(Elem *currentElem, Elem *preElem);
    bool NextElem(Elem *currentElem, Elem *nextElem);
    void ListTraverse();
    bool ListInsert(int i,Elem *e);
    bool ListDelete(int i,Elem *e);

private:
    int *m_pList; //指向一块内存
    int m_iSize; //内存多大
    int m_iLength;//线性表长度
};

#endif
#include"List.h"
#include<iostream>
using namespace std;

List::List(int size)
{
    m_iSize=size;
    m_pList = new int[m_iSize];
    m_iLength = 0;
}

List::~List()
{
    delete []m_pList;//释放数组
    m_pList=NULL;
}

void List::ClearList()
{
    m_iLength=0;
}

bool List::ListEmpty()
{
    if(m_iLength==0)
    {
        return true;
    }
    else{
        return false;
    }
    //return m_iLenght==0?true:false;
}

int List::ListLength()
{
    return m_iLength;
}

bool List::GetElem(int i,Elem *e)
{
    if(i<0||i>=m_iSize)
    {
        return false;
    }
    *e =m_pList[i];
    return true;
}

int List::LocateElem(Elem *e)
{
    for(int i=0;i<m_iLength;i++)
    {
        if(m_pList[i] ==*e)
        {
            return i;
        }
    }
    return -1;
}

bool List::PriorElem(Elem *currentElem, Elem *preElem)
{
    int temp= LocateElem(currentElem);
    if(temp==-1)
    {
        return false;
    }
    else
    {
        if(temp==0)//第一个位置没有前驱
        {
            return false;
        }
        else{
            *preElem = m_pList[temp-1];
            return true;
        }
    }
}

bool List::NextElem(Elem *currentElem, Elem *nextElem)
{
    int temp= LocateElem(currentElem);
    if(temp==-1)
    {
        return false;
    }
    else
    {
        if(temp==m_iLength-1)//最后一个元素没有后继
        {
            return false;
        }
        else{
            *nextElem = m_pList[temp+1];
            return true;
        }
    }
}

void  List::ListTraverse()
{
    for(int i=0;i<m_iLength;i++)
    {
        cout<<m_pList[i]<<endl;
    }
}

bool List::ListInsert(int i,Elem *e)
{
    if(i<0||i>m_iLength) // i=m_iLength即在线性表最后一个位置,不需要移动任何元素,直接插入即可
    {
        return false;
    }
    for(int k=m_iLength-1;k>=i;k--)//从后到前移动
    {
        m_pList[k+1]=m_pList[k];
    }
    m_pList[i]=*e;
    m_iLength++;
    return true;
}

bool List::ListDelete(int i,Elem *e)
{
     if(i<0||i>=m_iLength)//与上述有区别注意
    {
        return false;
    }
    *e= m_pList[i];
    for(int k=i+1;k<m_iLength;k++)//从前到后移动
    {
        m_pList[k-1]=m_pList[k];
    }
    m_iLength--;
    return true;
}

使线性表适用于其他类型,例如coordinate类型


  • 对于list的函数声明,把数据类型改为coordinate类即可 int LocateElem(Coordinate *e);
  • 需要修改的函数体:
  1. 遍历函数: 遍历时输出coordinate类型得元素,使用cout输出要提前重载操作符
    void  List::ListTraverse()
        {
            for(int i=0;i<m_iLength;i++)
            {
                cout<<m_pList[i]<<endl;//能否这样输出取决于是否重载了操作符
                //这样也可以:m_pLit[i].printCoordinate()
            }
        }
  1. 比较查找元素函数 :对于coordinate的==操作要重载
    int List::LocateElem(Coordinate *e)
    {
        for(int i=0;i<m_iLength;i++)
        {
            if(m_pList[i] ==*e)//需要堆coordiane做比较==运算符的重载
            {
                return i;
            }
        }
        return -1;
    }
  • 其他函数体不变
  • 如何重载?
class Coordinate
{
public:
    friend ostream &operator<<(ostream &out,Coordinate &coor);
    Coordinate(int x=0, int y=0);//默认构造函数
    void printCoordinate();
    bool operator==(Coordinate &coor);

private:
    int m_iX;
    int m_iY;
};
#include"Coordinate.h"
#include<iostream>

using namespace std;

Coordinate::Coordinate(int x,int y)
{
    m_iX=x;
    m_iY=y;
}

void Coordinate::printCoordinate()
{
    cout<<"("<<m_iX<<","<<m_iY<<")";
}

ostream &operator<<(ostream &out,Coordinate &coor)
{
    out<<"("<<coor.m_iX<<","<<coor.m_iY<<")";
    return out;
}

bool Coordinate::operator==(Coordinate &coor)
{
    if(this->m_iX==coor.m_iX&&this->m_iY==coor.m_iY)
    {
        return true;
    }
    else{
        return false;
    }
}

  • 测试:
// 线性表 顺序表
#include<iostream>
#include<stdlib.h>
#include"List.h"
using namespace std;

int main()
{
    //3 5 7 2 9 1 8
    Coordinate e1(3,5),e2=(5,7),e3=(6,8);
    List *list1=new List(10);

    list1->ListInsert(0,&e1);
    list1->ListInsert(1,&e2);
    list1->ListInsert(2,&e3);

    Coordinate temp;
   list1->ListTraverse();

    delete list1;

    system("pause");
    return 0;
}

在这里插入图片描述

链表


  1. 单链表:结点有指针域数据域
  2. 循环链表:最后一个结点指针域又指向头结点
  3. 双向链表:结点有数据域,两个指针域
  4. 静态链表:没有指针的情况下用数组完成
单链表实现:
#ifndef NODE_H
#define NODE_H

class Node
{
public:
    int data;
    Node *next;
    void printNode();
};

void Node::printNode()
{
    cout<<data<<endl;
}
#endif // NODE_H
#ifndef LIST_H
#define LIST_H

#include"Node.h"

class List
{
public:
    List();
    ~List();
    void ClearList();
    bool ListEmpty();
    int ListLength();
    bool GetElem(int i,Node *pNode);
    int LocateElem(Node *pNode);
    bool PriorElem(Node *pcurrentNode, Node *pPreNode);
    bool NextElem(Node *pcurrentNode, Node *pNextNode);
    void ListTraverse();
    bool ListInsert(int i,Node *pNode);//指定位置插入
    bool ListDelete(int i,Node *pNode);
    bool ListInsertHead(Node *pNode);
    bool ListInsertTail(Node *pNode);

private:
    Node *m_pList; //指向一块内存
    int m_iLength;//线性表长度
};
List::List()
{
    //定义一个头结点,通过头结点操控整个链表
    m_pList = new Node;
    m_pList->data = 0;
    m_pList->next=NULL;
    m_iLength=0;//该头结点置空,不算在链表长度之中
}

List::~List()
{
    //清除所有节点(包括头结点)
    ClearList();
    delete m_pList;
    m_pList=NULL;
}


void List::ClearList()
{
    //清除除了头结点所有节点,不断找下线删除
    Node *currentNode= m_pList->next;
    while(currentNode!=NULL)
    {
        Node *temp = currentNode->next;
        delete currentNode;
        currentNode=temp;
    }
    m_pList->next=NULL;
}

bool List::ListEmpty()
{
    if(m_iLength==0)
    {
        return true;
    }
    else{
        return false;
    }
}

int List::ListLength()
{
    return m_iLength;
}

 bool List::ListInsertHead(Node *pNode)
{
    Node *temp =m_pList->next;
    Node *newNode =new Node;//从堆中申请内存,从栈中申请函数执行完后内存会被回收掉,所以一定要从堆中申请内存
    if(newNode==NULL)
    {
        return false;
    }
    newNode->data=pNode->data;
    m_pList->next = newNode;//insert
    newNode->next=temp;
    m_iLength++;
    return true;
}

bool List::ListInsertTail(Node *pNode)
{
    Node *currentNode= m_pList;
    while(currentNode->next!=NULL)
    {
        currentNode=currentNode->next;
    }
    Node *newNode =new Node;//从堆中申请内存,从栈中申请函数执行完后内存会被回收掉,所以一定要从堆中申请内存
    if(newNode==NULL)
    {
        return false;
    }
    newNode->data=pNode->data;
    newNode->next=NULL;
    currentNode->next=newNode;
    m_iLength++;
    return true;
}

bool List::ListInsert(int i,Node *pNode)
{
    if(i<0||i>m_iLength)
    {
        return false;
    }
    Node *currentNode= m_pList;
    for(int k=0;k<i;k++)
    {
        currentNode=currentNode->next;
    }
    Node *newNode =new Node;
    if(newNode==NULL)
    {
        return false;
    }
    newNode->data=pNode->data;
    newNode->next=currentNode->next;
    currentNode->next=newNode;
    return true;
}

bool List::ListDelete(int i,Node *pNode)
{
    if(i<0||i>=m_iLength)
    {
        return false;
    }
    Node *currentNode=m_pList;
    Node *currentNodeBefore =NULL;
    for(int k=0;k<=i;k++)
    {
        currentNodeBefore=currentNode;
        currentNode=currentNode->next;
    }

    currentNodeBefore->next=currentNode->next;
    pNode->data=currentNode->data;
    delete currentNode;
    currentNode=NULL;
    m_iLength--;
    return true;
}

 bool List::GetElem(int i,Node *pNode)
 {
     if(i<0||i>=m_iLength)
    {
        return false;
    }
    Node *currentNode=m_pList;
    Node *currentNodeBefore =NULL;
    for(int k=0;k<=i;k++)
    {
        currentNodeBefore=currentNode;
        currentNode=currentNode->next;//找到第i个节点
    }
    pNode->data=currentNode->data;
    return true;
 }

int List::LocateElem(Node *pNode)
{
    Node *currentNode = m_pList;
    int count=0;//计数变量
    while(currentNode->next!=NULL)
    {
        currentNode=currentNode->next;
        if(currentNode->data==pNode->data)
        {
            return count;//若count为0就return即返回的是头结点后的第一个结点
        }
        count++;
    }
    return -1;
}

bool List::PriorElem(Node *pcurrentNode, Node *pPreNode)
{
    Node *currentNode = m_pList;
    Node *tempNode = NULL;
    while(currentNode->next!=NULL)
    {
        tempNode=currentNode;
        currentNode=currentNode->next;
        if(currentNode->data==pcurrentNode->data)
        {
            if(tempNode==m_pList)//如果前驱就是头结点,认定找不到该节点的前驱
            {
                return false;
            }
            pPreNode->data=tempNode->data;
            return true;
        }
    }
    return false;
}

bool List::NextElem(Node *pcurrentNode, Node *pNextNode)
{
    Node *currentNode = m_pList;
    while(currentNode->next!=NULL)
    {
        currentNode=currentNode->next;
        if(currentNode->data==pcurrentNode->data)
        {
            if(currentNode->next==NULL)//找到的当前结点已经是最后一个结点
            {
                return false;
            }
            pNextNode->data=currentNode->next->data;
            return true;
        }
    }
    return false;
}

void List::ListTraverse()
{
    Node *currentNode=m_pList;
    while(currentNode->next!=NULL)
    {
        currentNode=currentNode->next;
        currentNode->printNode();
    }
}
#endif
测试:
// ÏßÐÔ±í ˳Ðò±í
#include<iostream>
#include<stdlib.h>
#include"List.h"
using namespace std;

int main()
{
    Node node1,node2,node3,node4;
    node1.data=3;node2.data=4;node3.data=5;node4.data=6;

    Node node5;
    node5.data=7;
    List *pList = new List();

//    pList->ListInsertHead(&node1);
//    pList->ListInsertHead(&node2);
//    pList->ListInsertHead(&node3);
//    pList->ListInsertHead(&node4); //遍历后输出结果为 6 5 4 3

    pList->ListInsertTail(&node1);
    pList->ListInsertTail(&node2);
    pList->ListInsertTail(&node3);
    pList->ListInsertTail(&node4); //遍历后输出结果为 3 4 5 6

    pList->ListInsert(1,&node5);

    Node temp;
    //pList->ListDelete(1,&temp);

    pList->NextElem(&node5,&temp);
    pList->ListTraverse();

    cout<<"temp:"<<temp.data<<endl;

    delete pList;
    pList=NULL;


    system("pause");
    return 0;
}

在这里插入图片描述

链表应用——通讯录


  • 结点Node的data是person类型
  • newNode->data=pNode->data;对于这样data的赋值操作,要重载
  • f(currentNode->data==pNode->data)对于data之间的比较操作,要重载
  • void Node::printNode() { cout<<data<<endl; }对于打印节点操作,要重载cout

person.h

#ifndef PERSON_H_INCLUDED
#define PERSON_H_INCLUDED

#include<string>
#include<ostream>

using namespace std;

class Person
{
    friend ostream &operator<<(ostream &out,Person &person);
public:
    string name;
    string phone;
    Person &operator = (Person &person);
    bool operator ==(Person &person);
};

ostream &operator<<(ostream &out,Person &person)
{
    out<<person.name<<"----" <<person.phone<<endl;
    return out;
}

Person &Person::operator = (Person &person)
{
    this ->name = person.name;
    this->phone=person.phone;
    return *this;
}

bool Person::operator==(Person &person)
{
    if(this->name==person.name&&this->phone==person.phone)
    {
        return true;
    }
        return false;
}

#endif // PERSON_H_INCLUDED

测试
#include<iostream>
#include<stdlib.h>
#include"List.h"
using namespace std;

int main()
{

    Node node1;
    node1.data.name="sss";
    node1.data.phone="123456";
    Node node2;
    node2.data.name="sjx";
    node2
    .data.phone="238956";

    List *pList= new List();

    pList->ListInsertTail(&node1);
    pList->ListInsertTail(&node2);

    pList->ListTraverse();

    delete pList;
    pList=NULL;


    system("pause");
    return 0;
}

在这里插入图片描述

一切就绪之后,开始编写通讯录代码

通讯录.cpp


#include<iostream>
#include<stdlib.h>
#include"List.h"
using namespace std;

int menu()
{
    //显示通讯录功能菜单
    cout<<"功能菜单"<<endl;
    cout<<"1.新建联系人"<<endl;
    cout<<"2.删除联系人"<<endl;
    cout<<"3.浏览通讯录"<<endl;
    cout<<"4.退出通讯录"<<endl;

    cout<<"请输入:"<<endl;

    int order=0;
    cin>>order;

    return order;
}

void createPerson(List *pList)
{
    Node node ;
    Person person;
    cout<<"请输入姓名 :";
    cin>>person.name;
    cout<<"请输入电话 :";
    cin>>person.phone;
    node.data=person;
    pList->ListInsertTail(&node);
}
void deletePerson(List *pList,Node *temp)
{
   Node node;
   cout << "请输入要删除的联系人的姓名:" << endl;
   cin >> node.data.name;
   cout << "请输入要删除的联系人的电话:" << endl;
   cin >> node.data.phone;
   int locate = pList->LocateElem(&node);//先查找联系人的位置
   if(locate == -1)
   {
       cout << "没找到此联系人" << endl;
       return;
   }
   pList->ListDelete(locate,temp);//删除联系人
   cout << "成功删除联系人" << endl;
}

int main()
{
    int userOrder = 0;

    List *pList= new List();

    while(userOrder!=4)
    {
        userOrder = menu();
        Node temp;
        switch(userOrder)
        {
            case 1:
                cout<<"用户指令---->>新建联系人"<<endl;
                createPerson(pList);
                break;
            case 2:
                cout<<"用户指令---->>删除联系人"<<endl;
                deletePerson(pList,&temp);
                break;
           case 3:
                cout<<"用户指令---->>浏览通讯录"<<endl;
                pList->ListTraverse();
                break;
            case 4:
                cout<<"用户指令---->>退出通讯录"<<endl;
                break;
        }
    }

    delete pList;
    pList=NULL;
    return 0;
}


在这里插入图片描述

对于课程布置的删除作业,可参考https://www.imooc.com/qadetail/163402

;