C++和python实现单链表及其原理

目录
  • 一、链表的基本概念
  • 二、单链表
    • 1.python实现
      • (1)节点设计
      • (2)链表类:Single_Linked_List
      • (3)判断链表是否为空:is_empty()函数
      • (4)头插法:add()函数
      • (5)尾插法:append()函数
      • (6)在任意位置插入:insert()函数
      • (7)计算链表长度:get_length()函数
      • (8)遍历所有节点:traversal()函数
      • (9)搜索:search()函数
      • (10)删除:delete()函数
    • 2.C++实现
      • (1)节点设计
      • (2)链表类:SingleLinkedList
      • (3)判断链表是否为空:isEmpty()函数
      • (4)头插法:add()函数
      • (5)尾插法:append()函数
      • (6)在任意位置插入:insert()函数
      • (7)计算链表长度:getLength()函数
      • (8)遍历所有节点:traversal()函数
      • (9)搜索:search()函数
      • (10)删除:remove()函数

一、链表的基本概念

链表是数据元素的线性集合(Linear Collection),物理存储不连续。那么,这种设计的优点是什么?缺点又是什么?

链表的基本结构:

链表是由一系列的“节点”组成在一起的集合,节点(Node)由数据域(data)和指针域(next)组成。

从功能上看,data负责存储数据,next负责存储下一个节点的位置。当然,用更加严格的语句来讲,next存储的是其直接后继的地址,关于直接后继的定义见:

链表的分类:

常见的链表种类有:单向链表、双向链表、单向循环链表、双向循环链表,将会在后面文章中单独介绍各种链表的结构和代码实现,以及对应的链表操作。

链表的基本操作:

链表的基础操作包含:插入、删除、查找、合并等,此外还有:反转、排序、深度复制等。

链表的优点:

  • 插入和删除快;
  • 存储空间不受限制,可动态申请扩充,不需事先开辟内存;

链表的缺点:

  • 相比于数组,链表的需要更多的存储空间(需存储指针);
  • 存储不连续导致其访问时间长;
  • 从反向访问角度,单向链表难以反向访问,扩展为双向链表则需要额外存储反向指针;
  • 每次节点访问都需要从头部开始;

二、单链表

基本结构:

单链表的结构含有四个概念:头指针、头结点、普通Node、尾结点,下面分别介绍:

  • 头指针指向头结点,是单链表的表示,必不可少;
  • 头结点是单链表的第一个节点,其数据域内容一般无效;
  • 普通Node即用于数据存储和直接后继指针存储的节点;
  • 尾结点即链表中最后一个节点,其指针域next为空,其后无节点;

单链表的基本操作:

针对单链表常见的操作有:增、改、查、删等,

常用的操作如下:

(1)增

对链表添加元素一般有三种方法:头插法(add)、尾插法(append)、任意位置插入法(insert)。

(2)改

改动链表中某个节点的data

(3)查

查找分为按值查找和按位置查找两种,前者表示按照值查找对应位置,后者表示按位置查找对应值;

(4)删

删除分为按值删除和按位置删除两种;前者表示按照值删除对应节点,后者表示按照位置删除对应节点;

实现说明:

按照自己目前所看的资料,一般都会实现下面介绍的这些函数,具体介绍放在python和C++实现中。

1.python实现

(1)节点设计

按照单链表的定义可知,节点包含数据域data和指针域next

但是由于next和python的内置函数next()重名,所以指针域使用pointer表示。

代码如下:

class Node:
    def __init__(self, data):
        """
        Args:
            data: data of node, any type 
        """
        self.data = data
        self.pointer = None

(2)链表类:Single_Linked_List

上述Node类对象即为链表的基本组成结构,可以用于实现头结点、普通节点和尾结点。

因此,链表类只需要提供头指针:

class Single_Linked_List:
    def __init__(self, node=None):
        self.__head = node

(3)判断链表是否为空:is_empty()函数

实际上,只需要判断头指针是否指向Node类对象(或是否等于None),就可判断一个链表是否为空:

def is_empty(self):
    """判断链表是否为空"""
    if self.__head == None:
        return True
    else:
        return False

(4)头插法:add()函数

在链表头进行节点插入是很常见的插入操作,这种方式使得“先插入的节点在链表尾部”。头插法需要将头指针指向新的节点,并让新的节点指向原来的头结点:

def add(self, data):
    """Add dnode into head
    """ 
    # 创建新节点
    node = Node(data)
    # 令新的节点指向原来的头结点
    node.pointer = self.__head
    # 令头指针指向新的节点
    self.__head = node

(5)尾插法:append()函数

如果想要链表节点次序和插入次序相同,就需要使用尾插法。在插入之前需要判断链表是否为空,如果不为空才能进行插入(可以调用前面定义的is_empty()函数,但是下述代码没有)。

此外,还需要进行链表的遍历操作,找到最后一个节点。单链表只能从表头开始访问,所以每次尾插都必须遍历。

def append(self, data):
    """ append node into tail
    """
    node = Node(data)
    
    # 头指针为空时即为首节点
    if self.__head == None:
        self.__head = node
    # 头指针不为空时进行遍历
    else:
        current = self.__head
        while current.pointer != None:
            current = current.pointer
        current.pointer = node

(6)在任意位置插入:insert()函数

前面介绍的头插法和尾插法,其原理相对简单,但是并不能完全满足插入需求。如果知道目标插入的位置,可以采用insert()函数实现任意位置的节点插入。

需要注意的是,在实现insert()函数时必须考虑到“position”参数可能出现的几种情况。比如python中并没有明确的类型要求,所以要检查“position”是不是int类型。

对于核心的节点插入实现功能,需要找到目标插入位置对应的节点,并使得这个节点指向新节点,让新节点指向原位置节点的后一个节点。这个过程类似于铁链中加入铁环的过程,要保证新铁环和原来的两个铁环相连接。

def insert(self, position, data):
    """在任意位置插入节点
    Args:
        position:插入节点的位置,int
        data:插入节点的值
    """
    
    if not isinstance(position, int):
        raise ValueError("expect type is 'int', but got {}".format(position.__class__))
    
    # 头插法
    if position <= 0:
        self.add(data)
    # 尾插法
    elif position > self.get_length():
        self.append(data)
    
    else:
        current = self.__head
        current_position = 0
        node = Node(data)
        # 目的:计算出插入位置
        while current_position < position -1:
            current_position += 1
            current = current.pointer
        # 首先:必须使得当前节点的pointer指针指向新建的node
        # 其次:必须保证新建的node的pointer指向当前节点的后一个节点
        node.pointer = current.pointer
        current.pointer = node

(7)计算链表长度:get_length()函数

对于调用者和类内部的其它函数来做,链表长度是一个非常有用的值。比如在插入函数insert()中,需要判断插入位置是不是大于链表长度。

计算链表长度的实现比较简单,只需要遍历链表的所有节点,并用计数器来统计节点的数目即可。

def get_length(self):
    """ 获取链表的长度"""
    # 没有任何node
    if self.__head == None:
        return 0
    # 节点数统计
    else:
        current = self.__head
        length = 0
        while current != None:
            current = current.pointer
            length += 1
        return length

(8)遍历所有节点:traversal()函数

链表、树、图等结构都需要遍历操作,其中链表的遍历比较简单,只需要依次的访问所有节点即可。

def traversal(self):
    current = self.__head
    i = 0
    # 循环结束的条件依旧是节点的pointer指向不为空
    while current !=  None:
        print("Element {} is {} ".format(i, current.data))
        current = current.pointer
        i += 1

(9)搜索:search()函数

前面提到搜索有按值搜索和按位置搜索两种,它们的原理和实现都十分相似,所以仅以按值搜索为例。

需要注意的是,insert()函数需要判断链表是否为空,并且需要考虑到目标值不在链表中的情况,分别对应不同的返回值。

def search(self, data):
    """ 返回值为data的第一个节点"""
    if self.__head == None:
        return -1
    else:
        current = self.__head
        current_position = 0
        # 遍历节点
        while current != None:
            # 目标值搜索成功
            if current.data == data:
                return current_position
            # 目标值搜索不到则继续搜索       
            else:
                current_position += 1
                current = current.pointer
    # 目标值不存在于链表中         
    return False

(10)删除:delete()函数

上述的查找中以“按值查找”为例,这次删除中同样以“按值删除”为例,“按位置删除”的实现与之类似。

按值删除,即删除目标值对应的目标节点。在进行遍历时,需要记录当前节点和当前节点的前一个节点。因为,一旦查找大目标值所在的目标节点,需要令目标节点的前一个节点指向目标节点的下一个节点,即完成节点的删除。

def delete(self, data):
    """ 删除值为data的第一个节点"""
    if self.is_empty():
        return None
    
    # 记录当前节点和前一个节点
    current = self.__head
    piror = None
    while current != None:
        # 查找成功分为两种情况
        if current.data == data:
            # 目标节点为头结点
            if current == self.__head:
                self.__head = self.__head.pointer
                return True
             # 目标节点不是头结点
             # 令目标节点的前一个节点指向目标节点的后一个节点   
            else:
                piror.pointer = current.pointer
                return True
         # 更新当前节点和前一个节点
         else:
            piror = current
            current = current.pointer
    return False   

2.C++实现

前面的python实现中已经分析了各个函数的作用,以及对应的实现过程。虽然python和C++的语法不同,但是核心过程是类似的,所以下面不再重复对过程的叙述。

(1)节点设计

由于C++的指针必须指定类型,所以需要使用空指针NULL作为pointer的值。

class Node{
public:
    int data;
    Node *pointer=NULL;
};

(2)链表类:SingleLinkedList

遵循声明和实现分类的策略,先对各个函数进行声明。

class SingleLinkedList {
public:
    SingleLinkedList();
    bool isEmpty();
    int getLength();
    void add(int data);
    void append(int data);
    void insert(int position, int data);
    void traversal();
    int search(int data);
    void remove(int data);

private:
    Node *head;
};

(3)判断链表是否为空:isEmpty()函数

bool SingleLinkedList::isEmpty() {
    // 头结点不指向任何结点,为空
    if (head->pointer == NULL) {
        return true;
    }
    else {
        return false;
    }
}

(4)头插法:add()函数

void SingleLinkedList::add(int data) {
    // 当原列表仅有头结点时,直接插入新节点即可
    if (head->pointer == NULL) {
        head->pointer = new Node;
        head->pointer->data = data;
        
    }
    // 当原列表头结点后面含有后继节点时
    // 令头结点直接后继为新节点
    // 并令新节点的直接后继为原来头结点的直接后继
    else {
        // 临时存储头结点的直接后继
        Node *temp = head->pointer;
        head->pointer = new Node;
        head->pointer->data = data;
        head->pointer->pointer = temp;
    }
}

(5)尾插法:append()函数

void SingleLinkedList::append(int data) {
    Node *current = head->pointer;
    // 找到列表的最后一个节点的位置current
    // current的指针域为NULL
    while (current->pointer!=NULL)
    {
        current = current->pointer;
    }
    // 令current的指针域指向新节点,完成插入
    current->pointer = new Node;
    current->pointer->data = data;
}

(6)在任意位置插入:insert()函数

void SingleLinkedList::insert(int position, int data) {
    // 头插法
    if (position <= 0) {
        add(data);
    }
    // 尾插法
    else if (position > getLength()){
        append(data);
    }
    else {
        // 令头指针所在的位置为0
        int current_position = 0;
        Node *current = head;
        Node *prior = NULL;
        // 查找目标节点位置current,并记录其直接前驱节点piror
        while (current_position<position)
        {
            // 更新当前节点和直接前驱
            prior = current;
            current = current->pointer;
            current_position++;
        }
        // 目标位置的直接前驱prior指向新节点
        // 新节点指向目标位置的节点
        prior->pointer = new Node;
        prior->pointer->data = data;
        prior->pointer->pointer = current;
    }
};

(7)计算链表长度:getLength()函数

int SingleLinkedList::getLength() {
    int counter = 0;
    Node *current = head;
    // 遍历链表,直到最后一个元素
    while (current->pointer!=NULL)
    {
        counter++;
        current = current->pointer;
    }
    return counter;
}

(8)遍历所有节点:traversal()函数

void SingleLinkedList::traversal() {
    Node *current;
    // 指向头结点的直接后继
    current = head->pointer;
    int counter = 1;
    // 遍历链表,输出每个节点的值
    while (current!=NULL)
    {
        printf("Element in %d is %d \n", counter, current->data);
        counter++;
        current = current->pointer;
    }
}

(9)搜索:search()函数

int SingleLinkedList::search(int data) {
    int current_position = 1;
    Node *current = head->pointer;
    while (current!=NULL)
    {
        // 搜索成功返回当前位置
        if (current->data == data) {
            return current_position;
        }
        // 继续更新位置;
        current = current->pointer;
        current_position++;
    }
    // 搜索失败,返回-1
    return -1;
}

(10)删除:remove()函数

void SingleLinkedList::remove(int data) {
    Node *current = head->pointer;
    Node *prior = head;
    // 遍历链表
    while (current!=NULL)
    {
        // 查找到目标位置
        if (current->data == data) {
            // 令目标位置的直接前驱指向目标节点的直接后继
            prior->pointer = current->pointer;
            break;
        }
        // 更新当前节点和其前驱节点
        prior = current;
        current = current->pointer;
    }
}

总结:

在使用python实现时,头结点数据域data是有效的。这种方式使得代码中需要很多的“if-else”判断结构,增加了代码的复杂性。

在使用C++实现时,头结点数据域data是无效的,这种方式使得代码更加简洁。

到此这篇关于C++和python实现单链表及其原理的文章就介绍到这了,更多相关C++,python单链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++编程语言实现单链表详情

    目录 一.单链表简单介绍 二.下面我们先实现单链表的初始化. 三.实现单链表的插入与删除数据 一.单链表简单介绍 首先,我们再回顾一下线性表的两种存储方式--顺序存储与链式存储 上图左边为顺序存储,右边为链式存储 之前我们利用数组来实现顺序表,对于顺序表的优点缺点,总结来说就是,查找方便,增删复杂. 线性表之顺序存储的优缺点 而链表特点可以说恰恰相反,增删方便,查找复杂. 今天实现的是链表中最简单的一种--单链表(每个结点中只含有一个指针域) 对于链表我们只知道它每个结点的存储的物理地址是不连续

  • C++实现约瑟夫环的循环单链表

    约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知 n 个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.. 从编号为 k 的人开始报数,数到 m 的那个人出圈:他的下一个人又从 1 开始报数,数到 m 的那个人又出圈:依此规律重复下去,直到剩余最后一个胜利者.. 例如:有10个人围成一圈进行此游戏,每个人编号为 1-10 .. 若规定数到 3 的人出圈.. 则游戏过程如下.. (1)开始报数,第一个数到 3 的人为 3 号,3 号出圈.. 1, 2, [ 3 ], 4, 5, 6, 7

  • C++实现单链表的构造

    单链表的构造,包括最常用函数,setData(),Insert(),Remove(),getData(),Search(). 代码如下: #include <iostream> #include <stdlib.h> using namespace std; template<class T> struct LinkNode{ T data; LinkNode<T> *link; LinkNode(LinkNode<T> *ptr=NULL){l

  • C++数据结构之单链表

    目录 单链表结构的定义 单链表打印 动态申请一个结点 单链表尾插 单链表尾删 单链表头插 单链表头删 求单链表长度 单链表查找 单链表在pos位置插入 单链表在pos后面位置插入 单链表删除pos位置 单链表删除pos的下一个结点 判断单链表是否为空 头文件 源文件 简介: 线性表的顺序存储结构有一个缺点就是插入和删除时需要移动大量元素,这会耗费许多时间.能不能想办法解决呢?干脆所有的元素都不要考虑相邻位置了,哪有空位就到哪里,让每一个元素都知道它下一个元素的位置在哪里.线性表链式存储结构: 用

  • Python单链表原理与实现方法详解

    本文实例讲述了Python单链表原理与实现方法.分享给大家供大家参考,具体如下: Python实现单链表 关于链表 链表(Linked List)是由许多相同数据类型的数据项按照特定顺序排列而成的线性表. 链表中个数据项在计算机内存中的位置是不连续且随机的,数组在内存中是连续的. 链表数据的插入和删除很方便,但查找数据效率低下,不能像数组一样随机读取数据. 单链表的实现 一个单向链表的节点由数据字段和指针组成,指针指向下一个元素所在内存地址 定义一个链表节点类,self.value实例属性表示节

  • C++单链表实现大数加法

    本文实例为大家分享了C++单链表实现大数加法,供大家参考,具体内容如下 Input Format 输入文件包括两行. 第一行包括一个正整数,保证位数不超过1000000. 第二行包括一个正整数,保证位数不超过1000000. Output Format 输出文件包括一行. 第一行包括一个正整数. Sample Input 10558 22 Sample Output 10580 #include <iostream> using namespace std; class BigData { f

  • C++和python实现单链表及其原理

    目录 一.链表的基本概念 二.单链表 1.python实现 (1)节点设计 (2)链表类:Single_Linked_List (3)判断链表是否为空:is_empty()函数 (4)头插法:add()函数 (5)尾插法:append()函数 (6)在任意位置插入:insert()函数 (7)计算链表长度:get_length()函数 (8)遍历所有节点:traversal()函数 (9)搜索:search()函数 (10)删除:delete()函数 2.C++实现 (1)节点设计 (2)链表类

  • python实现单链表中删除倒数第K个节点的方法

    本文实例为大家分享了python实现单链表中删除倒数第K个节点的具体代码,供大家参考,具体内容如下 题目: 给定一个链表,删除其中倒数第k个节点. 代码: class LinkedListAlgorithms(object): def __init__(self): pass def rm_last_kth_node(self, k, linked_list): # 删除倒数第 K 个节点,针对单链表的 if linked_list.is_empty(): print 'The given li

  • python实现单链表的方法示例

    前言 首先说下线性表,线性表是一种最基本,最简单的数据结构,通俗点讲就是一维的存储数据的结构. 线性表分为顺序表和链接表: 顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,称为线性表的顺序存储结构或顺序映像: 链式表示指的是用一组任意的存储单元存储线性表中的数据元素,称为线性表的链式存储结构.而他既可以是连续的也可以不连续,是通过一个与后继结点的连接信息构建起来的. *顺序表(这个不是本次重点,简单介绍一下) 顺序表是用一段连续的存储单元依次存储数据元素,查找元素是很方便的,但是

  • python环形单链表的约瑟夫问题详解

    题目: 一个环形单链表,从头结点开始向后,指针每移动一个结点,就计数加1,当数到第m个节点时,就把该结点删除,然后继续从下一个节点开始从1计数,循环往复,直到环形单链表中只剩下了一个结点,返回该结点. 这个问题就是著名的约瑟夫问题. 代码: 首先给出环形单链表的数据结构: class Node(object): def __init__(self, value, next=0): self.value = value self.next = next # 指针 class RingLinkedL

  • python版单链表反转

    本文实例为大家分享了vue + element ui实现锚点定位的具体代码,供大家参考,具体内容如下 代码如下: class Node(object):     def __init__(self, elem, next_=None):         self.elem = elem         self.next = next_   def reverseList(head):     if head == None or head.next==None:  # 若链表为空或者仅一个数就

  • Python实现单链表中元素的反转

    给定一个单链表,将其反转.其实很容易想到,只需要修改每个结点的指针指向:即令后一个结点指向前一个结点,并且将表头指针指向最后一个结点即可. 这个过程可以用循环实现,也可以用递归来实现. 1.用循环来实现: class LNode:     def __init__(self, elem):         self.elem = elem         self.pnext = None   def reverse(head):     if head is None or head.pnex

  • python反转单链表算法题

    现在算法是大厂面试的必考题,而且越来越难,已经不是简单的列表,字符串操作了,会涉及到各种数据结结构.单链表的反转也是经常考的一道题,里面故在此记录一下. 1.链表的特点: 顺序存储元素,但是元素在空间上是不连续的,所以在链表每个元素中除了存储元素的值,还会存储下一个元素的地址,单链表的话就只有指向下一个元素的指针,双向链表的话还会有指向前一个元素的指针.正是由于链表以上的存储特点,在做插入和删除操作时只需要断开指针的连接,不需要移动后面的数据,所以对链表修改的效率会很高,但是查找的效率会很低,这

  • Python单链表简单实现代码

    本文实例讲述了Python单链表简单实现代码.分享给大家供大家参考,具体如下: 用Python模拟一下单链表,比较简单,初学者可以参考参考 #coding:utf-8 class Node(object): def __init__(self, data): self.data = data self.next = None class NodeList(object): def __init__(self, node): self.head = node self.head.next = No

  • Python单链表的简单实现方法

    本文实例讲述了Python单链表的简单实现方法,分享给大家供大家参考.具体方法如下: 通常来说,要定义一个单链表,首先定义链表元素:Element.它包含3个字段: list:标识自己属于哪一个list datum:改元素的value next:下一个节点的位置 具体实现代码如下: class LinkedList(object): class Element(object): def __init__(self,list,datum,next): self._list = list self.

随机推荐