美文网首页数据结构与算法
1 基本数据结构:数组与链表

1 基本数据结构:数组与链表

作者: GoFuncChan | 来源:发表于2020-02-17 01:54 被阅读0次

    线性表基本概念

    线性表是最基本、最简单、最常用的一种数据结构之一。在实际应用中,线性表都是以数组、字符串、链表、栈、队列、位图等特殊线性表的形式来使用的。因为这些特殊线性表都具有自己的特性,所以掌握这些特殊线性表的特性,对于数据运算的可靠性和提高操作效率是至关重要的。

    线性表.png

    线性表是一个线性结构,它是一个含有n≥0个节点的有限序列。在节点中,有且仅有一个开始节点没有前驱并有一个后继节点,有且仅有一个终端节点没有后继并有一个前驱节点。
    其他的节点都有且仅有一个前驱和一个后继节点。

    通常可以把一个线性表表示成一个线性序列:k1,k2,…,kn,其中k1是开始节点,kn是终端节点。

    1.线性结构的特征

    在编程领域中,线性结构具有如下两个基本特征:

    • (1)列表中必存在唯一的“第一元素”和唯一的“最后元素”。
    • (2)除最后一个元素之外,均有唯一的后继和唯一的前驱。

    由n(n≥0)个数据元素(节点)a1,a2,…,an组成的有限序列,数据元素的个数n定义为表的长度。

    当n=0时称为空表,我们通常将非空的线性表(n>0)记作:

    (a1,a2,…,an)数据元素ai(1≤i≤n)没有特殊含义,大家不必去“刨根问底”地研究它。它只是一个抽象的符号,其具体含义在不同的情况下可以不同。

    2.线性表的基本操作过程

    线性表虽然只是一个线性序列,但是其操作功能非常强大,具备了很多操作技能。

    线性表的基本操作过程如下所示:

    • (1)用Setnull(L)置空表。
    • (2)用Length(L)求表的长度和表中各元素的个数。
    • (3)用Get(L,i)获取表中的第i个元素(1≤i≤n)。
    • (4)用Prior(L,i)获取i的前趋元素。
    • (5)用Next(L,i)获取i的后继元素。
    • (6)用Locate(L,x)返回指定元素在表中的位置。
    • (7)用Insert(L,i,x)插入新元素。
    • (8)用Delete(L,x)删除已存在的元素。
    • (9)用Empty(L)来判断表是否为空。

    3.线性表的结构特点

    • 均匀性:虽然不同数据表的数据元素是各种各样的,但同一线性表的各数据元素必须有相同的类型和长度。
    • 有序性:各数据元素在线性表中的位置只取决于它们的序。数据元素之前的相对位置是线性的,

    即存在唯一的“第一个”和“最后一个”的数据元素,除了第一个和最后一个外,其他元素前面只有一个数据元素直接前趋,后面只有一个直接后继。

    4.两种实现线性表存储方式:

    1.顺序存储结构: 连续内存的线性存储结构,如数组、切片。

    顺序表有一个硬性规定,即用连续的存储单元顺序存储线性表中的各元素。
    根据这条硬性规定,当对顺序表进行插入和删除操作时,必须移动数据元素才能实现线性表逻辑上的相邻关系。
    很不幸的是,这种操作会影响运行效率。

    数组.jpg

    2.链式存储结构: 存储的值可以是不连续内存,每个节点都持有下一节点(或上一节点)的指针,链式存储结构不需要用地址连续的存储单元来实现,而是通过“链”建立起数据元素之间的次序关系。
    所以它不要求逻辑上相邻的两个数据元素在物理结构上也相邻,在插入和删除时无须移动元素,从而提高了运行效率。
    链式存储结构主要有单链表、循环链表、双向链表、静态链表等几种形式。

    链表.png

    Go中的数组和链表

    大多数语言在底层已经提供数组的实现,且是最基本的数据结构,是其他数据结构的基础。Go语言除数组外,更常用的是切片,在标准包中也提供了双向链表的包。这里为了演示线性表的这两种结构,我写了一些模拟代码:

    数组模拟

    
    // 自定义切片类型,切片底层为数组
    type ListArray []interface{}
    
    // 数组工厂方法
    func NewArray(n int) *ListArray {
        slice := make([]interface{}, 0, n)
        a := ListArray(slice)
        return &a
    }
    
    // 计算顺序表长度;
    func (a *ListArray) Len() int {
        return len(*a)
    }
    
    // 计算顺序表容量;
    func (a ListArray) Cap() int {
        return cap(a)
    }
    
    // 清空操作;
    func (a *ListArray) Clean() {
        *a = nil
    }
    
    // 判断是否为空;
    func (a *ListArray) IsEmpty() bool {
        return len(*a) == 0
    }
    
    // 判断是否为满;
    func (a *ListArray) IsFilled() bool {
        return len(*a) == cap(*a)
    }
    
    // 尾部追加
    func (a *ListArray) Append(element ...interface{}) {
        *a = append(*a, element...)
    }
    
    // 任意元素插入
    func (a *ListArray) Insert(i int, element ...interface{}) {
        slice := []interface{}(*a)
        if i > len(*a)-1 {
            slice[i] = element
            return
        }
    
        if i < 0 {
            i = 0
        }
        var b []interface{}
        b = append(slice[:i], element...)
        b = append(b, slice[i:]...)
        c := ListArray(b)
        *a = c
    }
    
    // 删除任意元素
    func (a *ListArray) Remove(i int) {
        slice := []interface{}(*a)
        b := append(slice[:i], slice[i+1:]...)
        c := ListArray(b)
        *a = c
    }
    
    // 按值查找
    func (a *ListArray) Contain(element interface{}) int {
        for k, v := range *a {
            if v == element {
                return k
            }
        }
        return -1
    }
    
    

    链表

    go标准包 container/list 已提供双向链表实现:
    常用操作:
    1.Init() 创建链表并初始化
    2.Len() 保存于链表结构字段中
    3.Front() 返回链表第一个元素
    4.Back() 返回链表最后一个元素
    5.Remove(element) 移除链表元素
    6.PushFront(v) 插入元素到表头
    7.PushBack(v) 插入元素到表尾
    8.InsertBefore(v,mark) 在某个元素前插入
    9.InsertAfter(v,mark) 在某个元素后插入
    10.MoveToFront(e) 移动元素到表头
    11.MoveToBack(e) 移动元素到表尾
    12.MoveBefore(e,mark) 移动元素到某个元素之前
    13.MoveAfter(e,mark) 移动元素到某个元素之后
    14.PushBackList() 从另一个链表拷贝元素追加到当前链尾
    15.PushFrontList() 从另一个链表拷贝元素追加到当前链头

    以下参照标准包修改一个单向链表的代码:

    
    // 实现单向链表
    type Element struct {
        next  *Element
        list  *List
        Value interface{}
    }
    
    // 链表元素迭代方法
    func (e *Element) Next() *Element {
        if p := e.next; e.list != nil && p != &e.list.root {
            return p
        }
        return nil
    }
    
    // 链表
    type List struct {
        root Element // 哨兵根节点
        len  int     // 维护一个长度属性
    }
    
    // 初始化
    func (l *List) Init() *List {
        l.root.next = &l.root
        l.len = 0
        return l
    }
    
    // 链表工厂方法
    func New() *List { return new(List).Init() }
    
    // 长度保存于链表结构字段中
    func (l *List) Len() int { return l.len }
    
    // 返回链表的第一个元素
    func (l *List) Front() *Element {
        return l.root.next
    }
    
    // 返回链表最后一个元素
    func (l *List) Back() *Element {
        e := l.root.next // root为哨兵头节点,第一个节点为root.next
        if e == &l.root {
            // 没有元素时还是返回root
            return e
        }
        for {
            if e.next == nil {
                return e
            }
            e = e.Next()
        }
    }
    
    // 内部方法:插入元素
    func (l *List) insert(e, at *Element) *Element {
        n := at.next
        at.next = e
        e.next = n
        e.list = l
        l.len++
        return e
    }
    
    // 内部方法:插入值
    func (l *List) insertValue(v interface{}, at *Element) *Element {
        return l.insert(&Element{Value: v}, at)
    }
    
    // 内部方法:删除元素
    func (l *List) remove(e *Element) *Element {
        var prev *Element
        ele := l.root.next // 第一个元素
        for {
            if ele.next == nil { // 尾元素
                return nil
            }
            if ele == e { // 找到该删除的元素
                prev.next = ele.next // 前一个元素接链
                l.len--
                return ele
            }
            prev = ele       // 暂存当前迭代元素
            ele = ele.Next() // 迭代下一个
        }
    
        return nil
    }
    
    // 内部方法,移动元素
    func (l *List) move(e, at *Element) *Element {
        if e == at {
            return e
        }
        n := at.next
        at.next = e
        e.next = n
    
        return e
    }
    
    // 删除链表元素
    func (l *List) Remove(e *Element) *Element {
        return l.remove(e)
    }
    
    // 插入元素到表头
    func (l *List) PushFront(v interface{}) {
        l.insertValue(v, &l.root)
    }
    
    // 插入元素到表尾
    func (l *List) PushBack(v interface{}) {
        var tail *Element
        for {
            e := l.root.Next()
            if e.next == nil {
                tail = e
                break
            }
        }
        l.insertValue(v, tail)
    }
    
    // 在某个元素前插入
    func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
        return l.insertValue(v, mark)
    }
    
    // 在某个元素后插入
    func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
        return l.insertValue(v, mark)
    }
    
    // 移动元素到表头
    func (l *List) MoveToFront(e *Element) {
        l.move(e,&l.root)
    }
    
    // 移动元素到表尾
    func (l *List) MoveToBack(e *Element) {
        var tail *Element
        for {
            e := l.root.Next()
            if e.next == nil {
                tail = e
                break
            }
        }
        l.move(e,tail)
    }
    
    // 移动元素到某个元素之前
    func (l *List) MoveBefore(e, mark *Element) {
        l.move(e,mark)
    }
    
    // 移动元素到某个元素之后
    func (l *List) MoveAfter(e, mark *Element) {
        l.move(e,mark)
    }
    
    // 从另一个链表拷贝元素追加到当前链尾
    func (l *List) PushList(other *List) {
        l.lazyInit()
        for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
            l.PushBack(e.Value)
        }
    }
    
    
    // lazyInit lazily initializes a zero List value.
    func (l *List) lazyInit() {
        if l.root.next == nil {
            l.Init()
        }
    }
    

    相关文章

      网友评论

        本文标题:1 基本数据结构:数组与链表

        本文链接:https://www.haomeiwen.com/subject/lsdwxhtx.html