[Data Structure]03.单向链表

相比起数组列表,单向链表更便于插入和删除(时间杂度更小),而且其内存位置较为灵活,在逻辑上(logic)是相连的,但是在物理上(phisical)却是不联通的。

链表的curr与数组列表略有所不同。链表的remove和assert动作,都是对curr指针后的一个数据/位置做出的变化。为了简化分类讨论,我们规定头结点head指针为空,里面不放任何元素数据,其实就是把它当作一个普通的结点。但要注意,tail的改变还是要分情况讨论的。tail与其他指针的不同之处在于,tail的next指向空。

List是储存数据的结构,node是组成list的部分,node需要另外定义成一个类。注意这里写了两个node的构造函数,是为了应对尾部结点和其他结点的两种情况。

今天整理耗时比之前都久一些,以下是我的代码:

#include <iostream>
using namespace std;
template <typename E> class node {
public:
    E num;
    node *next;
    node(node *ptr = NULL){next = ptr;} 
    //根据实际情况,这里需要两种构造函数
    node(E element, node* ptr = NULL ){
        num = element;
        next = ptr;}
    ~node(){}
};

template <typename E> class List{
private:
    node<E> *head; //head代表了一个链表
    node<E> *curr; //方便进行增删动作
    node<E> *tail;
    int length;
    void define(){ 
    //作为私有成员方便后面的成员函数调用,避免代码重复
        head = curr = tail = new node<E>();
        length = 0;
    }
    void clear()
    {
        while(head!=NULL)
        {
            curr = head;
            head = head->next;
            delete curr;
        }
    }
    
public:
    List(){ define(); }
    ~List(){clear();}
    void insert(const E it){
        node<E> *temp = new node<E>(it, curr->next);
        curr->next = temp;
        if(curr == tail) tail = curr->next; 
        //讨论curr为尾节点的情况
        length++;
    }
    void append(const E &it){ 
    //引用不占用内存
        node<E> temp = new node<E>(it,NULL);
        tail->next = &temp;
        tail = &temp;
        length++;
        //课本上这段代码只写了两行,感觉编者技巧特别娴熟
        //代码如下:
        //tail = tail->next = new node<E>(it,NULL);
        //length++;
    }
    E remove(){
        node<E> *temp = curr->next;
        E renum = temp->num;
        if(temp == tail) tail = curr; 
        //重新设置尾部结点(if needed)
        curr->next = temp->next;
        //delete temp;
        length--;
        return renum;
    }
    
    const int Aggregate(){ return length;}
    const E getValue(){
        return curr->next->num;
    }
    
    void moveptr(int dest){ 
    //简略了书上的MoveToStart等调整curr指针的函数
        curr = head;
        if(dest>=0&&dest<=length)
            for(int i = 0;i < dest; i++)
                curr = curr->next;
    }
};

int main() {
    //测试代码:
    List<int> mylist;
    mylist.insert(1);mylist.insert(2);mylist.insert(3);mylist.insert(4);
    mylist.append(5);
    //mylist.moveptr(1);
    int RemoveNum,Get,Length;
    Get = mylist.getValue();
    RemoveNum= mylist.remove();     
    Length = mylist.Aggregate();
    cout<<Get<<" "<<RemoveNum<<" "<<Length<<endl;
}

这段代码部分借鉴了我的课本(后附),课本中的写法其实更为精炼,我代码中的很多三、四句,课本都一句话就写完了。我感觉有以下几个原因:
1.为了节省空间,不设定多余变量,能省就省。
e.g:我代码中的temp等可以不设置出来,因为它是个中间量(intermediary),可有可无,缩写代码无非是省掉一个等式。
2.常用代码重用,重载/函数。
e.g:将define()和clear()写到私有类型中,在公有函数成员中反复调用,可以使代码整洁。

老师说注释最好也用英文写,下一次博客开始我尽量。。

reference:Data Structure and Algorithm Analysis

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值