线性表的链式存储 单链表C++实现

数据结构与算法 浙江大学

2.1线性表的链式存储

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

C语言实现

#include<iostream>
using namespace std;
typedef int ElementType; // ElementType 可定义为任意类型
typedef struct LNode* List;

//template<class ElementType>
struct LNode
{
    ElementType Data;
    List Next;
};
// List L;
List MakeEmpty(); //初始化链表
int Length(List L); //遍历链表求链表长度
List FindKth(int K, List L); //返回序号K的值
List Find(ElementType X, List L); // 按值查找
List Insert(ElementType X, int i, List L); //将 X插入到第 i-1(i>0)个结点之后
List Delete(int i, List L);
void Print(List L);

List MakeEmpty()
{
    List L = (List)malloc(sizeof(struct LNode));
    // List L = new LNode;
    L = nullptr;
    return L;
}

//求表长        
int Length(List L)
{
    List p = L;
    int len = 0;
    //从表头开始遍历,直到最后一个(nullptr),计数
    while (p)
    {
        p = p->Next;
        ++len;
    }
    return len;
}

List FindKth(int K, List L) //返回序号K的值
{
    List p = L;
    int len = 1; //从1开始
    while (p && len < K) //p为空指针时 或 当前序号>=K时 跳出循环
    {
        p= p->Next;
        len++;
    }
    if (len == K) //跳出循环后检查 是否找到K
    {
        return p;   
    }
    else    //没找到K 返回空指针
    {
        return nullptr;
    }
}

List Find(ElementType X, List L) // 按值查找
{
    List p = L;
    while (p && p->Data != X) //p为空指针时 或 当前值=X时 跳出循环
    {
        p = p->Next;
    }
    return p;
}
//将 X插入到第 i-1(i>0)个结点之后
List Insert(ElementType X, int i, List L) 
{
    List s, p;
    if (i == 1) //表头插入情况
    {
        // List s = MakeEmpty();//这个会保错。
        //插入s
        s = (List)malloc(sizeof(struct LNode));//申请内存
        s->Data = X; //给s赋值
        s->Next = L;
        return s;
    }
    //不是表头情况
    p = FindKth(i-1, L); // 找到i-1结点
    if (p) //找到i-1
    {
        //插入s
        s = (List)malloc(sizeof(struct LNode));
        s->Next = p->Next;
        s->Data = X;
        p->Next = s;
        return L;
    }
    else
    {
        cout << "结点错误" << endl;
		return nullptr;
    }
}
//将L的第i个元素删除,找到i-1个结点,i-1的next = i的next,释放i。
List Delete(int i, List L)
{
    List s,p;
    //删除表头,没有i-1的情况
    if (i == 1)
    {
        s = L;
        if (L != nullptr) //当前表不为空,删除表头
        {
            L = L->Next;
        }
        else{ //当前表为空,无法删除,返回空指针
            return nullptr;
        }
        free(s);
        return L;
    }
    //i不是表头的情况,找到i-1,改变Next的赋值实现删除
    p = FindKth(i-1, L);
    if (p == nullptr)
    {
        cout<<"结点"<<i-1<<"不存在"<<endl;
    }
    else if(p->Next == nullptr)
    {
        cout<<"结点"<<i<<"不存在"<<endl;

    }
    else{
        s = p->Next;
        p->Next = s->Next;
        free(s);
        return L;
    }
    
}

void PrintList(List L)
{
    List t;
	int flag = 1;
	cout << "当前链表为:";
	for (t = L; t; t = t->Next) 
	{
		cout << t->Data << " ";
		flag = 0;
	}
	if (flag)
		cout << "NULL";
	cout << endl;
}
int main()
{
    List L = MakeEmpty();
    PrintList(L);

    L = Insert(11, 1, L);
	L = Insert(25, 1, L);
	L = Insert(33, 2, L);
	L = Insert(77, 3, L);
	PrintList(L);
}

C++写成类实现 即单链表

仿STL中list的接口

#pragma once
#include <iostream>
#include <string>
#include <list>
using namespace std;

typedef int DataType;
// template<class DataType>
struct Node
{
    DataType data;
    Node *next;
};

//元素编号从1开始
// template<class DataType>
class LinkList
{
    public:
    LinkList() //无参构造
    {
        head = new Node;
        head = nullptr;
        // head->next = nullptr;
        this->size = 0;
    }
    LinkList(DataType elem)
    {
        head = new Node;
        head->data = elem;
        head->next = nullptr;
        this->size = 1;
    }
    LinkList(LinkList & list) //拷贝构造
    {
        this->head = new Node;
        this->head = list.head;
        this->size = list.size;
    }

    LinkList(int size, DataType elem=0)
    {
        head = new Node;
        head = nullptr;
        // head->next = nullptr;
        if (size > 0)//防止参数为负数
        {
            for (size_t i = 0; i < size; i++)
            {
                Push_back(elem);    
            }
        }
        this->size = Size();
    }
    ~LinkList();

    bool isEmpty()const; //链表是否为空
    int Size(); //通过遍历获取链表长度  
    int getLen()const; //返回size 
    int Display()const; //打印当前链表

    Node * FindKth(int K); //查找第K个结点  元素编号从1开始
    Node * Find(DataType X); //查找值为X的第一个结点
    Node * Front(); //获取第一个结点
    Node * Back(); //获取最后一个结点

    int Push_front(DataType elem); //头插
    int Push_back(DataType elem); //尾插
    int Insert(DataType elem, int pos, int num=1); //插入
    int Pop_front(); //头删
    int Pop_back(); //尾删
    int Erase(int beg, int end=0); //删除
    int Clear(); //清理链表
    int Remove(DataType elem); //删除容器中所有与elem值匹配的元素。
    int Reverse(); //链表前后翻转,倒置

    int size{0};//初始值为0
    Node *head;
};

LinkList::~LinkList()
{
    Clear();
}
//返回链表长度
int LinkList::Size()
{
    Node *currNode = head;
    int len = 0;
    while (currNode)
    {
        currNode = currNode->next;
        ++len;
    }
    this->size = len;
    return len;
}
//打印链表
int LinkList::Display() const
{
	cout << "当前链表元素为:";
	int flag = 1;
    int count = 0;
    Node *t;
    for (t = this->head; t; t = t->next) 
	{
		cout << t->data << " ";
		flag = 0;
        if (++count % 10 ==0)
        {
            cout<<endl;
        }
        
	}
	if (flag)
		cout << "NULL";
	cout << endl;
}
Node * LinkList::FindKth(int K)
{
    Node *currNode = head;
    int len = 1; //元素编号从1开始
    while (currNode && len < K)
    {
        currNode = currNode->next;
        ++len;
    }
    if (len == K)
    {
        return currNode;
    }
    else
    {
        return nullptr;
    }
}
Node * LinkList::Find(DataType X)
{
    Node *currNode = head;
    while (currNode && currNode->data != X)
    {
        currNode = currNode->next;
    }
    return currNode;
}

Node * LinkList::Front() //获取第一个结点
{
    return this->head;
}

Node * LinkList::Back() //获取最后一个结点
{
    return FindKth(this->size); //元素编号从1开始
}

int LinkList::Push_front(DataType elem)
{
    Insert(elem,1);
}
int LinkList::Push_back(DataType elem)
{
    if (isEmpty())
    {
        Insert(elem,1); //列表为空,相当于头插
    }
    else
    {
        Insert(elem,this->size + 1); //尾插,且序号从1开始,故插入到长度+1
    }
}
int LinkList::Clear()
{
    if (this->head == nullptr)
    {
        cout<<"链表当前已经为空,无需再clear "<<endl;
        return -1;
    }
    Node *ptemp;
    while (this->head)
    {
        ptemp = head->next;
        delete head;
        head = ptemp;
    }
    head = nullptr;
    head->next == nullptr;
    this->size = 0;
    cout<<"clear链表完成!"<<endl;
    return 0;
}

int LinkList::Insert(DataType elem, int pos, int num)
{
    //插入一个的情况
    if (1==num)
    {
        //需要插入的位置为pos,则需要将新结点的next指向结点pos,将结点pos-1的next指向新结点
        Node * pnewNode, *preNode;//待插入的新结点, 前一个结点
        //头插
        if (pos == 1)
        {
            //备份头结点
            preNode = this->head;
            //创建待插入的新节点
            pnewNode = new Node;
            pnewNode->data = elem;
            //新结点next指向头结点
            pnewNode->next = preNode;
            //更新头结点
            this->head = pnewNode;
            this->size++;
            return 0;
        }
        //非 头插
        preNode = FindKth(pos-1);  //元素编号从1开始
        if (preNode == nullptr)
        {
            cout << "不存在该结点" << pos-1 << endl;
            return -1;
        }
        else
        {
            //创建待插入的新节点
            pnewNode = new Node;
            pnewNode->data = elem;
            //新结点next指向 结点pos
            pnewNode->next = preNode->next;
            //将结点pos-1的next指向新结点
            preNode->next = pnewNode;
        }
        this->size++;
        return 0;    
    }
    //异常情况
    else if (num < 1)
    {
        cout<<"插入个数错误"<<endl;
        return -1;
    }
    // 插入多个的情况,递归调用自己即可
    else
    {
        for (size_t i = 0; i < num; i++)
        {
            Insert(elem,pos++);
        }
    }
    return 0;
}

int LinkList::Pop_front() //头删
{
    return Erase(1);
}
int LinkList::Pop_back() //尾删
{
    return Erase(this->size);
}

int LinkList::Erase(int beg, int end)
{
    //入参是区间的情况,
    if (end)
    {
        //计算删除次数
        int count = min(end-beg, this->size-beg) + 1;//防止end越界
        if (count <= 0) //防止 end 大于 beg
        {
            cout << "beg 需小于 end"<<endl;
            return -1;
        }
        //开始删除
        for (size_t i = 0; i < count; i++)
        {
            Erase(beg); //连续删除时,删除的是相同的位置
        }
        return 0;
    }
    //入参是一个的情况,即删除一个
    else
    {
        //需要删除的结点为beg,则结点beg-1的next 指向 结点beg+1,并释放结点i。考虑边界情况
        Node * currNode, *preNode;//待删除的结点, 前一个结点
        //头删
        if (1 == beg)
        {
            if (!isEmpty())
            {
                //备份需要删除的结点
                currNode = this->head;
                //head结点后移
                this->head = this->head->next;
                this->size--;
                //释放已删除的结点
                delete currNode;
                return 0;
            }
            else
            {
                cout<<"无法删除,列表为空列表"<<endl;
                return -1;
            }    
        }
        //非 头删
        preNode = FindKth(beg-1);
        if (preNode && preNode->next)
        {
            //备份需要删除的结点
            currNode = preNode->next;
            //结点i-1的next 指向 结点i+1
            preNode->next = currNode->next;
            //释放已删除的结点
            delete currNode;
            this->size--;
            return 0;
        }
        else
        {
            cout<<"无该结点,无法删除"<<endl;
            return -1;
        }
    }
}

int LinkList::getLen()const
{
    return this->size;
}
bool LinkList::isEmpty()const
{
    if (this->head == nullptr) //头指针为空,链表为空
    {
        return true;
    }
    else
    {
        return false;
    }  
}
int LinkList::Remove(DataType elem) //删除容器中所有与elem值匹配的元素。
{
    Node * currNode = head;
    int len = 1;
    while (currNode)
    {
        if (currNode->data == elem) //找到则删除,并继续往后,且len不变
        {
            Erase(len);
            // currNode指向的结点已删除,需要重新赋值
            currNode = FindKth(len);
        }
        else //未找到则并继续往后
        {
            len ++;
            currNode = currNode->next;
        }
    }
    return 0;
}
int LinkList::Reverse() //链表前后翻转,倒置
{
    if (size <= 1)
    {
        return -1;
    }
    Node *preNode = head, *currNode = preNode->next, *temp = nullptr;
    //第一个结点断开 即next指向空
    head->next = nullptr;
    while (currNode) //currNode为最后一个结点的next时,跳出循环
    {
        //备份curr的next
        temp = currNode->next;
        //替换curr的next为前一个结点
        currNode->next = preNode;
        //更新preNode currNode两个指针
        preNode = currNode;
        currNode = temp;
    }
    //最后一个结点赋值给头指针,
    head = preNode;
    //翻转完成,依旧通过头指针访问链表
}
//一些测试
void test()
{
    cout<<"STL list<int>"<<endl;
    list<int> l1(5,5);
    l1.front();
    l1.back();
    l1.push_back(1);
    l1.push_back(2);
    l1.push_back(3);
    l1.push_front(1);
    l1.push_front(2);
    l1.push_front(3);
    l1.pop_back();
    list<int> l2;
    cout<<"l1.size "<<l1.size()<<endl;
    cout<<"l1.size "<<l2.size()<<endl;
    cout<<"-------"<<endl;
}

void test002()
{
    cout<<"my list"<<endl;
    LinkList mylist(5,5);
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    cout<<mylist.Size()<<endl;

    mylist.Push_front(1);
    mylist.Push_front(2);
    mylist.Push_front(3);
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    mylist.Push_back(1);
    mylist.Push_back(2);
    mylist.Push_back(3);
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    mylist.Insert(6,4);
    mylist.Insert(6,4);
    mylist.Insert(6,4);
    mylist.Insert(6,12,3);
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    mylist.Insert(6,100);
    mylist.Insert(6,100,-1);

    mylist.Pop_back();
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    mylist.Pop_back();
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    mylist.Pop_back();
    mylist.Display();
    cout<<mylist.getLen()<<endl;

    mylist.Pop_front();
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    mylist.Pop_front();
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    mylist.Pop_front();
    mylist.Display();
    cout<<mylist.getLen()<<endl;

    mylist.Erase(1);
    mylist.Erase(2);
    mylist.Erase(3);
    mylist.Display();
    cout<<mylist.getLen()<<endl;

    mylist.Erase(7,2);
    mylist.Erase(6,8);
    mylist.Display();
    cout<<mylist.getLen()<<endl;

    mylist.Remove(5);
    mylist.Display();
    cout<<mylist.getLen()<<endl;
    cout<<mylist.Size()<<endl;
}
void test003()
{
    int * ptrnull = NULL;
    cout << "ptrnull "<< ptrnull <<endl;
    int * ptr = new int(1);
    cout << "ptr "<< ptr <<endl;
    cout << "*ptr "<< *ptr <<endl;
    delete ptr;
    cout<<"delete ptr:"<<endl;
    cout << "ptr "<< ptr <<endl;
    cout << "ptr "<< ptr <<endl;
    cout << "*ptr "<< *ptr <<endl;
    cout << "*ptr "<< *ptr <<endl;
    ptr = nullptr;
    int* ptr4 =new int;
    cout << "ptr4 "<< ptr4 <<endl;
    cout << "*ptr4 "<< *ptr4 <<endl;
    ptr = new int(13);
    cout << "ptr "<< ptr <<endl;
    cout << "*ptr "<< *ptr <<endl;
}
void test004()
{
    LinkList mylist;
    mylist.Push_back(1);
    mylist.Push_back(2);
    mylist.Push_back(3);
    mylist.Push_back(4);
    mylist.Push_back(5);
    mylist.Push_back(6);
    mylist.Display();
    mylist.Reverse();
    mylist.Display();   
}
int main()
{
    test004();
    return 0;
}

链表倒置的流程图
在这里插入图片描述

参考链接
1 数据结构(二)—— 线性结构(1):线性表
2 单链表C++实现代码
3 单链表的C++实现(采用模板类)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值