单向链表

单向链表解析
本文介绍了单向链表的基本概念,包括链表的结构、创建方法(头插法和尾插法)、表长求解、查找操作(按序号和按值查找)、插入和删除操作等,并分析了各种操作的时间复杂度。

1.从实现的角度看,链表可分为动态链表和静态链表;从连接方式的角度看,链表可分为单向链表、循环链表和双向链表。

2.单向链表的结点结构

(1)数据域data

(2)指针域link

3.我们通常用头指针来标识一个单向链表。头指针为NULL则表示一个空链表。

4.一个链表包含了零个、一个或多个结点,因此一个LinkedList的对象包含有零个、一个或多个ListNode的对象。这种关系在面向对象中叫聚合关系。可以用复合类来表达这种关系:利用在ListNode类中声明友元类的方法,让ListNode类和LinkedList类的成员都能访问ListNode类的私有成员。

5.单向链表的类定义

//文件node.h,结点类ListNode
#ifndef ListNode_
#define ListNode_

template <class T> class LinkedList;       	//前视定义,否则友元无法定义
template <class T>                        	//模板类型为T

class ListNode
{
    friend class LinkedList<T>;           	//定义类LinkedList<T>为友元
private:
    ListNode<T> *link;                    	//指向下一结点的指针
    T data;                               	//定义为私有成员
public:
    //构造函数1,用于构造头结点
    ListNode(ListNode<T> *ptrlink = NULL)
    { link = ptrlink; }

    //构造函数2,用于构造其他结点
    ListNode(const T& item, ListNode<T> *ptrlink = NULL)
    { data = item; link = ptrlink; }

    ~ListNode(void){}                		//析构函数
    //以item和next建立一个新结点
    ListNode<T> * getNode(const T& item, ListNode<T> *next = NULL)
    ListNode<T> * getLink() { return link; }  	//取得结点的下一结点地址
    T getData() { return data; }             	//取得结点中的数据
    void setLink(ListNode<T> * next) { link = next; }	//修改结点的link域
    void setData(T value) { data = value; }        	//修改结点的data值    

};
#endif

//文件LinkedList.h,链表的类定义
#ifndef LinkedList_
#define LinkedList_

#include <iostream>
#include "node.h"

template<class T>
class LinkedList 
{
public:
    LinkedList() { first = 0; }
    ~LinkedList();
    bool IsEmpty() const { return first = = 0; }
    int Length() const;
    bool Find(int k, T& x) const;
    int Search(const T& x) const;
    ListNode<T> *First() { return first; }
    LinkedList<T>& Delete(int k, T& x);
    LinkedList<T>& Insert(int k, const T& x);
    void Output(ostream& out) const;
private:
    ListNode<T> *first;  //头指针
};
...
#endif

单向链表上基本操作的实现

1.建立单向链表

(1)头插法

//创建一个单向链表, finished是停止建表输入标志, 是所有输入值中不可能出现的数值
template <class Type>
LinkedList <Type> :: LinkedList()
{  
    ListNode<Type> *p;
    first =  NULL;	//创建空表
    Type value;	
    cin >> value;   
    while (value != finished)  
    {  
        p= new ListNode<Type>(value, first);
        first = p;
        cin >> value;  
    }
}


(2)尾插法

//创建一个单向链表, finished是停止建表输入标志, 是所有输入值中不可能出现的数值
template <class Type>
LinkedList <Type>::LinkedList() 
{ 
    ListNode<Type> *p, *rear;
    Type value;	
    first = NULL;
    rear = NULL;
    cin >> value;   
    while (value != finished) 
    {
        p= new ListNode<Type>(value, NULL);
        if (first == NULL)  first = p;  	//第一个结点的处理 
        else  rear->link = p;       		//其他结点的处理 
        rear = p;        					//r指向新的尾结点
        cin >> value;   
    }
    if (rear != NULL)  rear->link = NULL; //对非空表, 最后结点的指针域放空指针 
}

为解决第一个结点的问题,增加一个头结点。


2.求表长

//返回链表中元素的个数
//移动指针current和计数器len
template<class T>
int LinkedList<T>::Length() const
{   
    ListNode<T> *current = first;
    int len = 0;
    while (current) 
    {
        len++;
        current = current->link;
    }
    return len;
}
时间复杂度为O(n)

3.查找

(1)按序号查找

//查找第k个元素用x返回, 查找成功返回true, 否则返回false
template<class T>
bool LinkedList<T>::Find(int k, T& x) const
{   
    if (k < 1) return false;
    ListNode<T> *current = first;
    int index = 1;  //当前索引
    while (index < k && current) 
    {
        current = current->link;
        index++;
    }
    if (current) 
    {
        x = current->data;
        return true;
    }
    return false; //没有第k个元素
}

(2)按值查找,即定位

//如果找到 x, 返回其位序, 否则返回0
template<class T>
int LinkedList<T>::Search(const T& x) const
{   
    ListNode<T> *current = first;
    int index = 1;  //当前索引
    while (current && current->data != x) 
    {
        current = current->link;
        index++;
    }
    if (current) return index;
    return 0;
}


上述两个算法的时间复杂度均为O(n)


4.插入

(1)后插结点

将*s插入到*current的后面

s->link=current->link;
current->link=s;
(2)前插结点

将*s插入到*current的前面

q=first;
while(q->link!=current)
    q=q->link;  //找*current的直接前驱
s->link=q->link;
q->link=s;

后插操作的时间复杂度是O(1),前插操作的复杂度是O(n)。也可将*s插入到*current的后面,然后将current->data与s->data交换,时间复杂度降为O(1)。

//在第k个元素后插入一个元素值为x的结点
template<class T>
LinkedList<T>& LinkedList<T>::Insert(int k, const T& x)
{
    if (k < 0) throw OutOfBounds();

    //查找第k个结点, 用p指向它
    ListNode<T> *p = first;
    for (int index = 1; index < k && p; index++) 
        p = p->link;
    if (k > 0 && !p) throw OutOfBounds(); //没有第k个结点

    //插入
    ListNode<T> *s = new ListNode<T>;
    s->data = x;
    if (k) 
    {	//插在*p后面
        s->link = p->link;
        p->link = s;
    }
    else 
    {		//如果k==0, 插入作为第一个元素
        s->link = first;
        first = s;
    }
    return *this;
}

时间复杂度是O(n)

5.删除

(1)删除*current

找到前驱*current的前驱结点*q

q->link=current->link;
delete current;
//时间复杂度O(n)

(2)删除*current的后继结点(假设存在)

s=current->link;
current->link=s->link;
delete s;
//时间复杂度O(1)

删除操作的具体算法

//删除第k个元素, 值用x返回
template<class T>
LinkedList<T>& LinkedList<T>::Delete(int k, T& x)
{
    if (k < 1 || !first) throw OutOfBounds(); //没有第k个元素
   
    //查找第k个结点, 用p指向它
    ListNode<T> *p = first;

    if (k == 1) 	//p已经是第k个元素
        first = first->link; //从链中删除
    else 
    { 	//用q记录第k-1元素
        ListNode<T> *q = first;
        for (int index = 1; index < k - 1 && q; index++)
            q = q->link;
        if (!q || !q->link) throw OutOfBounds();	//没有第k个元素
        p = q->link; 	//记录被删结点
        q->link = p->link;
    } 		//从链中删除
    //保存第k个元素并且回收结点
    x = p->data;
    delete p;
    return *this;
}
时间复杂度为O(n)





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值