数据结构-线性表

数据结构-线性表

一、线性表概述

1.1 线性表介绍

1.1.1 线性表定义
  • 线性表是n(n>=0)个数据元素的有限序列
1.1.2 线性表特点
  • 在数据元素的非空有限集中:
  1. 在唯一的一个被称作第一个的数据元素
  2. 存在唯一的一个被称作最后一个的数据元素
  3. 除第一个元素之外、集合中的每个元素均只有一个前驱
  4. 除最后一个元素之外、集合中的每个元素均只有一个后继

二、线性表的实现方式

2.1 顺序表

2.1.1 顺序存储定义
  • 用一段地址连续的存储单元依次存储线性表的数据元素
2.1.2 顺序表存储方式
  • 可以使用一维数据来实现顺序存储结构,线性表相邻的元素存放在数组相邻的位置
    • 根据数组长度是否可变可分为:
      1. 定长线性表
      2. 变长线性表
2.1.3 顺序表特性
  1. 逻辑上相邻的数据,在物理存储的位置上也是相邻的
  2. 可以实现随机访问,存取访问时间复杂度为 O ( 1 ) O(1) O(1)
  3. 存储密度高,需要预先分配空间
  4. 不便于插入和删除。当顺序表较大时,插入和删除都会引起大量数据的移动(时间复杂度为 O ( n ) O(n) O(n))。
  5. 由于难以确定表的长度,所以定义数组大小时可能会需要频繁扩容或有空间大量冗余

2.2 链表

2.2.1 链表定义
  • 链表是一种物理上非连续、非顺序的存储结构
2.2.2 结点
(1)结点定义
  • 为了表示每个数据元素与它的后继数据元素的逻辑关系,对于数据元素a来说,除了存储其本身的信息之外(数据域),还需要存储指示其直接后继的存储位置(指针域)。
  • 这两部分信息组成数据元素a的存储映像,称为结点(Node)
(2) 结点分类
  1. 后继节点(NextNode)
    • 结点后一个结点称为该结点后继节点
  2. 前继结点(PreNode)
    • 结点前一个结点称为该结点前继节点
2.2.3 链表特性
  • 对于一般链表包含以下特点
  1. 链表相对于顺序表每个结点需要额外位置保存其它结点位置信息,占用空间更多
  2. 访问链表元素时,需要从链表头指针或尾指针(双向链表)链表遍历查找,时间复杂度为 O ( n ) O(n) O(n)
  3. 链表在物理存储上不连续,因此可以对空间利用率上更高
  4. 对于插入删除操作只需该表相应位置前后继结点的指向,时间复杂度为 O ( 1 ) O(1) O(1)
2.2.4 链表分类
(1)单向链表
介绍
  • 最简单的链表结构,每个结点包含一个数据域与指向后继结点的指针(引用),尾结点的指针(引用)指向空结点
  • 一般对于完整单向链表结构包含:
    • 头结点
    • 其它辅助数据
特点
  • 只能实现从头遍历访问,效率不高
(2)双向链表
介绍
  • 相对于单链表而言,每个结点还包含一个指向该结点前继结点的指针(引用)
  • 一般对于完整双向链表结构包含:
    • 头结点
    • 尾结点
    • 其它辅助数据
特点
  • 不仅可以从前往后进行遍历访问,还可以从某个结点往前遍历访问,从任意结点都可以访问到其他节点
  • 对于某些操作可以先判断要操作的结点位置更加靠近尾部还是头部,以便决定从尾部遍历还是从头部遍历,时间耗费可接近为单链表的一半
(3)循环链表
介绍
  • 相对于单链表而言,尾结点指针(引用)指向头结点
  • 一般对于完整循环链表结构包含:
    • 头结点
    • 其它辅助数据
特点
  • 从任意结点都可以访问到其他节点,相对于双向链表,访问该结点之前的结点可能需要循环一圈,效率较低
(4)以上链表的组合或其他

三、线性表的实现

3.1 线性表操作接口

3.1.1 线性表接口java实现
package com.niss.list;

/**
 * @author Ni187
 */
public interface List<T> {
    /**
     * 判断是否为空
     * @return 是否为空
     */
    boolean isEmpty();

    /**
     * 线性表元素个数
     * @return 线性表元素个数
     */
    int size();

    /**
     * 清空链表
     * @return 操作是否成功
     */
    boolean clear();

    /**
     * 从前往后获得参数元素位置
     * @param data 元素
     * @return 参数元素位置
     */
    int indexOf(T data);

    /**
     * 从后往前获得第一个参数元素位置
     * @param data 元素
     * @return 参数元素位置
     */
    int lastIndexOf(T data);

    /**
     * 在index位置插入元素
     * @param index 插入位置
     * @param data 插入元素
     * @return 是否插入成功
     */
    boolean insert(int index,T data);

    /**
     * 向线性表末尾添加元素
     * @param data 元素
     * @return 操作是否成功
     */
    boolean add(T data);

    /**
     * 删除线性表中的元素
     * @param index 被删除元素位置
     * @return 是否删除成功
     */
    boolean remove(int index);

    /**
     * 设置第index个元素为data
     * @param index 要置换的元素位置
     * @param data 要置换的对象
     * @return 原先的元素
     */
    T set(int index,T data);

    /**
     * 获得元素
     * @param index 元素位置
     * @return 第index个元素
     */
    T get(int index);

}


3.2 顺序表实现

3.2.1 顺序表操作部分操作
(1) 插入
  • 插入
    顺序表-插入
  • 插入先扩容
    顺序表-插入-扩容
(2) 删除
  • 删除
3.2.2 顺序表Java实现
package com.niss.list;

import java.util.Arrays;

/**
 * @author Ni187
 */

public class ArrayList<E> implements List<E> {

    /**
     * 初始化大小
     */
    private final static int INIT_SIZE = 10;

    /**
     * 当前已存储元素长度
     */
    private int size;

    Object[] objects;


    public ArrayList() {
        this(INIT_SIZE);
    }

    public ArrayList(int size) {
        if (size < 0) {
            throw new IndexOutOfBoundsException("初始化大小不能小于零!");
        }
        this.objects = new Object[size];
        this.size = 0;
    }

    /**
     * 由数组创建一个线性表
     * @param objects 数组
     * @return 线性表
     */
    public static ArrayList newArrayList(Object[] objects){
        ArrayList arrayList = new ArrayList(0);
        arrayList.objects = objects;
        arrayList.size = objects.length;
        return arrayList;
    }

    /**
     * 扩展数组大小
     * @return 操作是否成功
     */
    private boolean resize() {
        int newSize = objects.length + objects.length<<2;
        Object[] newObjs = new Object[this.objects.length + newSize];
        System.arraycopy(this.objects, 0, newObjs, 0, this.objects.length);
        this.objects = newObjs;
        return true;
    }

    @Override
    public boolean isEmpty() {
        return this.size == 0;
    }

    @Override
    public int size() {
        return this.size;
    }

    @Override
    public boolean clear(){
        try {
            Arrays.fill(objects, null);
            this.size = 0;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 分析:时间复杂度O(n)
     */
    @Override
    public int indexOf(E data) throws NullPointerException {
        if (data == null) {
            for (int i = 0; i < this.size; i++) {
                if (objects[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = 0; i < this.size; i++) {
                if (objects[i].equals(data)) {
                    return i;
                }
            }
        }
        return -1;
    }

    @Override
    public int lastIndexOf(E data) {
        if (data == null) {
            for (int i = this.size - 1; i >= 0; i--) {
                if (objects[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = this.size - 1; i >= 0; i--) {
                if (objects[i].equals(data)) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 将传入元素插入到相应位置
     * @param index 插入位置
     * @param data 元素
     * @return 插入结果
     */
    @Override
    public boolean insert(int index, E data) {
        //如果位置不合法
        if(index<0||index>this.size){
            throw new IndexOutOfBoundsException("位置参数不合法");
        }
        try {
            //如果数组正好全部被占用,重设大小
            if (this.size >= objects.length-1) {
                this.resize();
            }

            for (int i = this.size; i > index; i--) {
                this.objects[i] = this.objects[i-1];
            }
            this.objects[index] = data;
            this.size++;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 添加元素到线性表尾部
     * @param data 插入元素
     * @return 操作结果
     */
    @Override
    public boolean add(E data){
        return this.insert(this.size, data);
    }

    @Override
    public boolean remove(int index) {
        if(index<0||index>=this.size){
            throw new IndexOutOfBoundsException("位置参数不合法");
        }
        try{
            for(int i = index+1;i<this.size-1;i++){
                objects[i-1] = objects[i];
            }
            --this.size;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    @Override
    public E set(int index, E data) {
        E oldObj = (E) this.objects[index];
        return oldObj;
    }

    @Override
    public E get(int i){
        if(i>=this.size||i<0){
            throw new IndexOutOfBoundsException("越界");
        }
        return (E)objects[i];
    }

    /**
     * 弹出最后一个元素
     * @return 最后一个元素
     */
    public E pop(){
        if(this.size>0){
            --this.size;
            Object data = objects[this.size];
            objects[this.size] = null;
            return (E)data;
        }
        return null;
    }


    @Override
    public String toString(){
        return Arrays.toString(objects);
    }

}

3.3 链表实现

3.3.1 链表部分操作实现
(1) 插入
  • 靠队头
    双向链表-插入-靠队头

  • 靠队尾

    双向链表-插入-靠队尾

(2) 访问
  • 靠队头
    双向链表-访问-靠队头
  • 靠队尾
    双向链表-访问-靠队尾
(3)删除
  • 靠队头
    双向链表-删除-靠队头
3.3.2 单链表实现
(1)Java实现
package com.niss.list;


/**
 * @author Ni187
 */

public class LinkedList<E extends Comparable<E>> implements List<E> {

    /**
     * 节点类
     *
     * @param <E>
     */
    private static class Node<E>{
        E elem;
        Node<E> next;

        public Node(E elem, Node<E> next) {
            this.elem = elem;
            this.next = next;
        }

        public Node() {
            this(null, null);
        }

        public static<E> void swapElem(Node<E> n1,Node<E> n2){
            E temp = n1.elem;
            n1.elem = n2.elem;
            n2.elem = temp;
        }
    }

    /**
     * 头节点
     */
    private Node<E> head;

    /**
     * 单链表长度
     */
    private int size;

    public LinkedList() {
        this.head = new Node<E>();
    }

    @Override
    public boolean isEmpty() {
        return this.head.next == null;
    }

    @Override
    public int size() {
        Node<E> currentNode = this.head;
        int count = 0;
        while (currentNode.next != null) {
            currentNode = currentNode.next;
            count++;
        }
        return count;
    }

    @Override
    public boolean clear(){
        Node<E> current = head.next;
        head.next = null;
        while(current!=null){
            Node<E> next = current.next;
            current.elem = null;
            current.next = null;
            current = next;
        }
        return true;
    }

    @Override
    public int indexOf(E data) {
        int index = 0;
        Node<E> currentNode = this.head;
        while(currentNode.next!=null){
            currentNode = currentNode.next;
            if(currentNode.elem.equals(data)){
                return index;
            }
            index++;
        }
        return -1;
    }

    @Override
    public int lastIndexOf(E data) {
        return this.size-this.indexOf(data);
    }

    @Override
    public boolean insert(int index, E data) {
        checkInsertIndex(index);
        Node<E> currentNode = this.head;
        for(int i = 0;i < index;i++){
            currentNode = currentNode.next;
        }
        currentNode.next = new Node<E>(data,currentNode.next);
        this.size++;
        return true;
    }

    @Override
    public boolean add(E data) {
        return this.insert(this.size, data);
    }

    @Override
    public boolean remove(int index) {
        checkQueryIndex(index);
        Node<E> currentNode = this.head;
        for(int i = 0;i < index;i++){
            currentNode = currentNode.next;
        }
        currentNode.next = currentNode.next.next;
        return true;
    }


    @Override
    public E set(int index, E data) {
        checkQueryIndex(index);
        Node<E> currentCode = this.head;
        for (int i = 0; i < index+1; i++) {
            currentCode = currentCode.next;
        }
        E original = currentCode.elem;
        currentCode.elem = data;
        return original;
    }

    @Override
    public E get(int index) {
        checkQueryIndex(index);
        Node<E> currentNode = this.head;
        for(int i = 0;i <= index;i++){
            currentNode = currentNode.next;
        }
        return currentNode.elem;
    }

    /**
     * 检查查询位置是否合法
     * @param index 查询位置
     */
    private void checkQueryIndex(int index){
        if(index<0||index>=this.size){
            throw new RuntimeException("查找位置越界!");
        }
    }

    /**
     * 检查插入位置是否合法
     * @param index 插入位置
     */
    private void checkInsertIndex(int index){
        if(index<0||index>this.size){
            throw new RuntimeException("插入位置越界!");
        }
    }

    /**
     * 获得新的反转链表(浅拷贝)
     * @return 新的反转链表
     */
    public LinkedList<E> getReversedList(){
        LinkedList<E> linkedList = new LinkedList<E>();
        if(head.next==null){
            return linkedList;
        }
        //当前节点
        Node<E> current = head.next;
        //上一个节点
        Node<E> preNode = null;
        while(current!=null){
            //复制一个新节点,值为当前节点的值
            Node<E> newNode = new Node<E>(current.elem,current.next);
            //新节点的后继节点为上一节点
            newNode.next = preNode;
            //上一节点设为当前节点
            preNode = newNode;
            //后移
            current = current.next;
        }
        linkedList.head.next = preNode;
        linkedList.size = this.size;
        return linkedList;
    }

    /**
     * 反转当前单链表
     */
    public void reverse(){
        if(head.next==null){
            return ;
        }
        //当前节点
        Node<E> current = head.next;
        //上一个节点
        Node<E> preNode = null;
        while(current!=null){
            //新节点的后继节点为上一节点
            Node<E> next = current.next;
            current.next = preNode;
            //上一节点设为当前节点
            preNode = current;
            //后移
            current = next;
        }
        head.next = preNode;
    }

    /**
     * 反转链表-递归算法
     * @param node 当前链表头结点
     */
    private void reverse0(Node<E> node){
        //如果已经到最后一个节点或者第一个节点就为空,头节点后继节点指向该节点返回
        if(node == null||node.next==null){
            this.head.next = node;
            return ;
        }
        //临时当前节点后继节点
        Node<E> next = node.next;
        //处理后继节点的倒置
        reverse0(next);
        //后继节点的后继节点指向该节点
        next.next = node;
        //本节点的后继节点设为null,保证最后一个节点为null
        node.next = null;
    }

    public void reverse0(){
        reverse0(this.head.next);
    }

    /**
     * 合并有序链表
     * @param l1 有序单链表l1
     * @param l2 有序单链表l2
     * @param <E> 实现Comparable接口,可以比较的类
     * @return 合并后的有序链表
     */
    public static<E extends Comparable<E>> LinkedList<E> mergeList(LinkedList<E> l1,LinkedList<E> l2){
        Node<E> head = l1.head;
        Node<E> current = l1.head;
        Node<E> c1 = l1.head.next;
        Node<E> c2 = l2.head.next;
        if(c1==null){
            return l2;
        }
        if(c2==null){
            return l1;
        }
        while(c1!=null&&c2!=null){
            if(c1.elem==null?c2.elem==null:c1.elem.compareTo(c2.elem) < 0){
                current.next = c1;
                c1 = c1.next;
            }else{
                current.next = c2;
                c2 = c2.next;
            }
            current = current.next;
        }
        if(c1==null){
            current.next = c2;
        }
        if(c2==null){
            current.next = c1;
        }
        LinkedList<E> linkedList = new LinkedList<E>();
        linkedList.size = l1.size+l2.size;
        l1 =l2 = null;
        linkedList.head = head;
        return linkedList;
    }

    public static<E extends Comparable<E>> LinkedList<E> recursionMergeList(LinkedList<E> l1,LinkedList<E> l2){
        Node<E> node = recursionMergeNode(l1.head.next, l2.head.next);
        LinkedList<E> linkedList = new LinkedList<E>();
        linkedList.head.next = node;
        return linkedList;
    }

    /**
     * 递归合并有序链表-递归算法
     * @param node1 有序单链表表头结点的后继结点
     * @param node2 有序单链表l2表头结点的后继结点
     * @param <E> 实现Comparable接口,可以比较的类
     * @return 合并后的有序链表的头结点
     */
    private static<E extends Comparable<E>> Node<E>recursionMergeNode(Node<E> node1,Node<E> node2){
        if(node1==null){
            return node2;
        }
        if(node2==null){
            return node1;
        }
        if(node1.elem==null?node2.elem==null:node1.elem.compareTo(node2.elem) < 0){
            node1.next = recursionMergeNode(node1.next, node2);
            return node1;
        }else{
            node2.next = recursionMergeNode(node1, node2.next);
            return node2;
        }
    }

    @Override
    public String toString(){
        StringBuilder str = new StringBuilder("[");
        Node<E> currentNode = this.head;
        while(currentNode.next!=null){
            currentNode = currentNode.next;
            if(currentNode == head.next){
                str.append(currentNode.elem);
            }else {
                str.append(", ").append(currentNode.elem);
            }
        }
        str.append("]");
        return str.toString();
    }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值