数据结构与算法:线性表-------Java实现链表、栈、队列

定义

  • 线性表:具有相同特性数据元素的一个有限序列;该序列中所包含的元素个数n为线性表的长度(n >= 0 )。ps:当n=0时线性表为空表
  • 线性表的存储结构有:
    • 顺序存储结构(顺序表):用一段地址连续的存储单元依次存储线性表的数据元素。
    • 链式存储结构(链表):用地址连续或不连续的存储单元存储线性表数据元素。

分类

顺序表

  • 顺序表即是将线性表中的所有元素按照其逻辑顺序,依次存储到从指定的存储位置开始的一块连续的存储空间中。
  • 特性:
    • 随机访问
    • 占用连续的存储空间
    • 存储分配只能预先进行(静态分配,即是初始化时指定大小)
    • 插入操作需要移动多个元素

数组

在这里插入图片描述

  • 什么是数组呢?

数组是一种线性表数据结构;它用一组连续的内存空间,来存储一组具有相同类型的数据。(顺序表)

要点:数组是线性表(即数据排列像一条线一样的结构,每个线性表上的数据最多只有前和后两个方向),数组使用连续的内存空间存储相同类型的数据。

  • 数组有什么特点?
    • 支持随机访问,根据下标随机访问的时间复杂度为O(1)
    • 可以存储基本数据类型(Java中的ArrayList无法存储基本数据类型如:int,其存储int型数据时需要将数据封装为Integer)
    • 表示多维数组时会更加直观
  • 数组下标为什么是从0开始?而不是从1开始?
    • 数组的下标其实就是偏移量(offset),用array表示数组首地址时,array[0]就是偏移量为0的位置,也是首地址;array[m]就是偏移量为m的位置,array[m]的地址是:base_address + m * type_size
    • 如果数组的下标从1开始,那么array[n]元素的地址为:base_address+(n-1) * type_size,这时每次随机访问数组时都会多一次减法运算,CPU就会多执行一次减法指令。

链表

  • 定义:链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包含两个部分:

    • 数据域:存储数据元素data ;
    • 指针域:存储后继指针next(存储下一个结点地址);
  • 特性:

    • 不支持随机访问
    • 存储空间可动态分配
    • 结点的存储空间利用率比顺序表低(需要部分空间用于存储后继指针next
    • 插入操作无需移动元素

链表通过指针将一组零散的内存块串联起来,其中的内存块即为“结点

单链表

在这里插入图片描述

  • Node结点类:
package com.linkedlist.dto;

/**
 * @Author: xpengfei
 * @Date: 2020/6/6 23:35
 * @Description:节点实体类
 */
public class Node {

    /**
     * 节点数据域,用于存储数据
     */
    public int data;
    /**
     * 节点的指针域,用于存储下一个节点的位置
     */
    public Node next;

    /**
     * 初始化node时给数据域赋值
     * @param data
     */
    public Node(int data) {
        this.data = data;
    }

    /**
     * 初始化node时给数据域及指针域赋值
     * @param data
     * @param next
     */
    public Node(int data, Node next) {
        this.data = data;
        this.next = next;
    }
}

  • 添加节点

尾部添加

		// 要添加的节点
        Node node = new Node(addData);
        if (head == null) {
            head = node;
        } else {
            Node tempNode = head;
            while (tempNode.next != null) {
                tempNode = tempNode.next;
            }
            // 将要添加的节点,添加到链表尾部
            tempNode.next = node;
        }

头部添加:

		// 要添加的节点
  		Node node = new Node(addData);
        if (head == null) {
            head = node;
        } else {
            Node temp = head;
            node.next = temp;
            head = node;
        }
  • 删除节点
	/**
     * 获取链表长度
     *
     * @return
     */
    public int getLinkedListLen() {
        if (head == null) {
            return 0;
        }
        int len = 0;
        Node temp = head;
        while (temp != null) {
            len++;
            temp = temp.next;
        }
        return len;
    }

    /**
     * 删除链表中第index个节点
     *
     * @param index 要删除的节点序号
     */
    public void deleteNodeByIndex(int index) {
        if (head == null || index <= 0 || index > this.getLinkedListLen()) {
            return;
        } else {
            if (index == 1) {
                head = head.next;
                return;
            }
            Node temp = head;
            while (temp != null) {
                index--;
                if (index == 1) {
                    temp.next = temp.next.next;
                    break;
                }
                temp = temp.next;
            }
        }
    }
  • 插入
/**
     * 将数据data插入到链表中的第index个位置
     *
     * @param data  待插入数据
     * @param index 待插入的位置
     */
    public void insertDataToLinkedListByIndex(int data, int index) {

        if (index < 0) {
            return;
        }
        if (head == null) {
            head = new Node(data);
            return;
        }
        // index 大于链表长度,插到链表尾部
        if (index > this.getLinkedListLen()) {
            Node node = new Node(data);
            Node tempNode = head;
            while (tempNode.next != null) {
                tempNode = tempNode.next;
            }
            //插入到链表尾部
            tempNode.next = node;
            node.next = null;
            return;
        }
        //插入到链表中间
        Node node = new Node(data);
        Node tempNode = head;
        while (tempNode != null) {
            index--;
            // 找到待插入位置的前一个node结点,index=0表示要插到链表头结点位置
            if (index == 0 || index == 1) {
                break;
            }
            tempNode = tempNode.next;
        }
        if (index == 0) {
            node.next = tempNode;
            head = node;
        } else {
            node.next = tempNode.next;
            tempNode.next = node;
        }
    }

  • 查找
/**
     * 查找数据在链表中第一次出现的位置
     *
     * @param nodeHead 链表的头结点
     * @param data     待查找数据
     * @return 返回数据在链表中的位置,-1表示链表中不存在待查找数据
     */
    public int findNodeIndex(Node nodeHead, int data) {
        int index = 0;
        while (nodeHead != null) {
            ++index;
            if (nodeHead.data == data) {
                return index;
            }
            nodeHead = nodeHead.next;
        }
        return -1;
    }
  • 反转
 /**
     * 反转链表
     * @return 反转后的链表的头结点
     */
    public Node reverseLinkedList() {
        Node currentNode = head;
        Node preNode = null;
        while (currentNode != null) {
            Node next = currentNode.next;
            currentNode.next = preNode;
            preNode = currentNode;
            currentNode = next;
        }
        return preNode;
    }

单链表操作相关代码:


package com.linkedlist;

import com.linkedlist.dto.Node;

/**
 * @Author: xpengfei
 * @Date: 2020/6/6 22:20
 * @Description:单链表
 */
public class SingleLinkedList {

    /**
     * 头结点
     */
    protected Node head = null;

    /**
     * 添加节点,链尾插入节点
     *
     * @param addData 要添加的节点数据
     */
    public Node tailAddNode(int addData) {
        // 要添加的节点
        Node node = new Node(addData);
        if (head == null) {
            head = node;
        } else {
            Node tempNode = head;
            while (tempNode.next != null) {
                tempNode = tempNode.next;
            }
            // 将要添加的节点,添加到链表尾部
            tempNode.next = node;
        }
        return node;
    }

    /**
     * 添加节点,头部插入节点
     *
     * @param addData 要添加的节点数据
     */
    public Node headAddNode(int addData) {
        Node node = new Node(addData);
        if (head == null) {
            head = node;
        } else {
            Node temp = head;
            node.next = temp;
            head = node;
        }
        return node;
    }

    /**
     * 获取链表长度
     *
     * @return
     */
    public int getLinkedListLen() {
        if (head == null) {
            return 0;
        }
        int len = 0;
        Node temp = head;
        while (temp != null) {
            len++;
            temp = temp.next;
        }
        return len;
    }

    /**
     * 删除链表中第index个节点
     *
     * @param index 要删除的节点序号
     */
    public void deleteNodeByIndex(int index) {
        if (head == null || index <= 0 || index > this.getLinkedListLen()) {
            return;
        } else {
            if (index == 1) {
                head = head.next;
                return;
            }
            Node temp = head;
            while (temp != null) {
                index--;
                if (index == 1) {
                    temp.next = temp.next.next;
                    break;
                }
                temp = temp.next;
            }
        }
    }


    /**
     * 降序排序
     * 冒泡排序
     */
    public void sortByDesc() {
        // 小于等于1个节点时无需排序
        if (this.getLinkedListLen() <= 1) {
            return;
        }
        Node currentNode;
        Node nextNode;
        for (currentNode = head; currentNode.next != null; currentNode = currentNode.next) {
            for (nextNode = head; nextNode.next != null; nextNode = nextNode.next) {
                if (nextNode.next.data > nextNode.data) {
                    int tempData = nextNode.next.data;
                    nextNode.next.data = nextNode.data;
                    nextNode.data = tempData;
                }
            }
        }
    }

    /**
     * 升序排序
     * 冒泡排序
     */
    public void sortByAsc() {
        // 小于等于1个节点时无需排序
        if (this.getLinkedListLen() <= 1) {
            return;
        }
        Node currentNode;
        Node nextNode;
        for (currentNode = head; currentNode.next != null; currentNode = currentNode.next) {
            for (nextNode = head; nextNode.next != null; nextNode = nextNode.next) {
                if (nextNode.data > nextNode.next.data) {
                    int tempData = nextNode.data;
                    nextNode.data = nextNode.next.data;
                    nextNode.next.data = tempData;
                }
            }
        }
    }

    /**
     * 查找数据在链表中第一次出现的位置
     *
     * @param nodeHead 链表的头结点
     * @param data     待查找数据
     * @return 返回数据在链表中的位置,-1表示链表中不存在待查找数据
     */
    public int findNodeIndex(Node nodeHead, int data) {
        int index = 0;
        while (nodeHead != null) {
            ++index;
            if (nodeHead.data == data) {
                return index;
            }
            nodeHead = nodeHead.next;
        }
        return -1;
    }


    /**
     * 将数据data插入到链表中的第index个位置
     *
     * @param data  待插入数据
     * @param index 待插入的位置
     */
    public void insertDataToLinkedListByIndex(int data, int index) {

        if (index < 0) {
            return;
        }
        if (head == null) {
            head = new Node(data);
            return;
        }
        // index 大于链表长度,插到链表尾部
        if (index > this.getLinkedListLen()) {
            Node node = new Node(data);
            Node tempNode = head;
            while (tempNode.next != null) {
                tempNode = tempNode.next;
            }
            //插入到链表尾部
            tempNode.next = node;
            node.next = null;
            return;
        }
        //插入到链表中间
        Node node = new Node(data);
        Node tempNode = head;
        while (tempNode != null) {
            index--;
            // 找到待插入位置的前一个node结点,index=0表示要插到链表头结点位置
            if (index == 0 || index == 1) {
                break;
            }
            tempNode = tempNode.next;
        }
        if (index == 0) {
            node.next = tempNode;
            head = node;
        } else {
            node.next = tempNode.next;
            tempNode.next = node;
        }
    }

    /**
     * 反转链表
     *
     * @return 反转后的链表的头结点
     */
    public Node reverseLinkedList() {
        Node currentNode = head;
        Node preNode = null;
        while (currentNode != null) {
            Node next = currentNode.next;
            currentNode.next = preNode;
            preNode = currentNode;
            currentNode = next;
        }
        return preNode;
    }

    /**
     * 打印出链表中的所有节点
     */
    public void printLinkList() {
        System.out.println("--------------------链表输出开始----------------------");
        Node temp = head;
        while (temp != null) {
            System.out.print(temp.data + "\t");
            temp = temp.next;
        }
        System.out.println();
        System.out.println("--------------------链表输出结束----------------------");
    }
}

单链表操作单元测试:

package com.linkedlist;

import com.linkedlist.dto.Node;
import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * @Author: xpengfei
 * @Date: 2020/6/7 14:35
 * @Description:
 */
public class SingleLinkedListTest {

    /**
     * 链表添加节点
     * 测试尾插法
     */
    @Test(priority = 1)
    public void tailAddNode() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.tailAddNode(1);
        singleLinkedList.tailAddNode(2);
        singleLinkedList.tailAddNode(3);
        singleLinkedList.tailAddNode(4);
        singleLinkedList.tailAddNode(5);
        singleLinkedList.tailAddNode(6);
        singleLinkedList.printLinkList();
        Assert.assertTrue(true);
    }

    /**
     * 链表添加节点
     * 测试头插法
     */
    @Test(priority = 2)
    public void headAddNode() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.headAddNode(1);
        singleLinkedList.headAddNode(2);
        singleLinkedList.headAddNode(3);
        singleLinkedList.headAddNode(4);
        singleLinkedList.headAddNode(5);
        singleLinkedList.headAddNode(6);
        singleLinkedList.printLinkList();
        Assert.assertTrue(true);
    }

    /**
     * 测试获取链表长度
     */
    @Test(priority = 3)
    public void getLinkedListLen() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.headAddNode(1);
        singleLinkedList.headAddNode(2);
        singleLinkedList.headAddNode(3);
        singleLinkedList.headAddNode(4);
        singleLinkedList.headAddNode(5);
        singleLinkedList.headAddNode(6);
        Assert.assertEquals(singleLinkedList.getLinkedListLen(), 6);
    }

    /**
     * 测试删除节点
     * 删除的index小于链表长度
     */
    @Test(priority = 4)
    public void deleteNodeByIndex() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.headAddNode(1);
        singleLinkedList.headAddNode(2);
        singleLinkedList.headAddNode(3);
        singleLinkedList.headAddNode(4);
        singleLinkedList.headAddNode(5);
        singleLinkedList.headAddNode(6);
        singleLinkedList.deleteNodeByIndex(0);
        Assert.assertEquals(singleLinkedList.getLinkedListLen(), 6);
    }

    /**
     * 测试删除节点
     * 删除第1个
     */
    @Test(priority = 5)
    public void deleteNodeByIndex0() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.headAddNode(1);
        singleLinkedList.headAddNode(2);
        singleLinkedList.headAddNode(3);
        singleLinkedList.headAddNode(4);
        singleLinkedList.headAddNode(5);
        singleLinkedList.headAddNode(6);
        singleLinkedList.deleteNodeByIndex(1);
        Assert.assertEquals(singleLinkedList.getLinkedListLen(), 5);
    }

    /**
     * 测试删除节点
     * 删除第3个
     */
    @Test(priority = 6)
    public void deleteNodeByIndex1() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.headAddNode(1);
        singleLinkedList.headAddNode(2);
        singleLinkedList.headAddNode(3);
        singleLinkedList.headAddNode(4);
        singleLinkedList.headAddNode(5);
        singleLinkedList.headAddNode(6);
        singleLinkedList.deleteNodeByIndex(3);
        Assert.assertEquals(singleLinkedList.getLinkedListLen(), 5);
    }

    /**
     * 测试删除节点
     * 删除最后一个
     */
    @Test(priority = 7)
    public void deleteNodeByIndex2() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.headAddNode(1);
        singleLinkedList.headAddNode(2);
        singleLinkedList.headAddNode(3);
        singleLinkedList.headAddNode(4);
        singleLinkedList.headAddNode(5);
        singleLinkedList.headAddNode(6);
        singleLinkedList.deleteNodeByIndex(6);
        Assert.assertEquals(singleLinkedList.getLinkedListLen(), 5);
    }

    /**
     * 测试删除节点
     * 删除的index大于链表长度
     */
    @Test(priority = 8)
    public void deleteNodeByIndex3() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.headAddNode(1);
        singleLinkedList.headAddNode(2);
        singleLinkedList.headAddNode(3);
        singleLinkedList.headAddNode(4);
        singleLinkedList.headAddNode(5);
        singleLinkedList.headAddNode(6);
        singleLinkedList.deleteNodeByIndex(1000);
        Assert.assertEquals(singleLinkedList.getLinkedListLen(), 6);
    }

    /**
     * 测试降序排序
     * 冒泡
     */
    @Test(priority = 9)
    public void sortByDesc() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.tailAddNode(1);
        singleLinkedList.tailAddNode(8);
        singleLinkedList.tailAddNode(3);
        singleLinkedList.tailAddNode(6);
        singleLinkedList.tailAddNode(9);
        singleLinkedList.tailAddNode(2);
        System.out.println("*****************排序之前******************");
        singleLinkedList.printLinkList();
        System.out.println("*****************排序之后******************");
        singleLinkedList.sortByDesc();
        singleLinkedList.printLinkList();
    }

    /**
     * 升序排序
     * 冒泡
     */
    @Test(priority = 10)
    public void sortByAsc() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.tailAddNode(1);
        singleLinkedList.tailAddNode(8);
        singleLinkedList.tailAddNode(3);
        singleLinkedList.tailAddNode(6);
        singleLinkedList.tailAddNode(9);
        singleLinkedList.tailAddNode(2);
        System.out.println("*****************排序之前******************");
        singleLinkedList.printLinkList();
        System.out.println("*****************排序之后******************");
        singleLinkedList.sortByAsc();
        singleLinkedList.printLinkList();
    }

    /**
     * 链表反转
     */
    @Test(priority = 11)
    public void reverseLinkedList() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.tailAddNode(1);
        singleLinkedList.tailAddNode(8);
        singleLinkedList.tailAddNode(3);
        singleLinkedList.tailAddNode(6);
        singleLinkedList.tailAddNode(9);
        singleLinkedList.tailAddNode(2);
        System.out.println("**************链表反转之前*************");
        singleLinkedList.printLinkList();
        Node reversedHeadNode = singleLinkedList.reverseLinkedList();
        System.out.println("**************链表反转之后********************");
        while (reversedHeadNode != null){
            System.out.print(reversedHeadNode.data+"\t");
            reversedHeadNode = reversedHeadNode.next;
        }
        System.out.println();
    }

    /**
     * 查找数据第一次在链表中出现的位置
     */
    @Test(priority = 12)
    public void findNodeIndex() {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.tailAddNode(9);
        singleLinkedList.tailAddNode(2);
        singleLinkedList.tailAddNode(8);
        singleLinkedList.tailAddNode(13);
        singleLinkedList.tailAddNode(10);
        singleLinkedList.tailAddNode(6);
        Assert.assertEquals(singleLinkedList.findNodeIndex(singleLinkedList.head,13),4);
    }

    /**
     * 测试插入
     */
    @Test(priority = 13)
    public void insertDataToLinkedListByIndex(){
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        System.out.println("****************插入之前***链表为空****0******************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之前***链表为空****0******************");
        singleLinkedList.insertDataToLinkedListByIndex(66,-2);
        System.out.println("****************插入之后***链表为空,插入位置为负****0******************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之后***链表为空,插入位置为负****0******************");

        System.out.println("****************插入之前***链表为空****1******************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之前****链表为空***1******************");
        singleLinkedList.insertDataToLinkedListByIndex(1,2);
        System.out.println("****************插入之后*****index>length****1****************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之后****index>length*****1****************");

        System.out.println("****************插入之前*****链表不为空**2******************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之前******链表不为空*2******************");
        singleLinkedList.insertDataToLinkedListByIndex(23,6);
        System.out.println("****************插入之后***index>length****index>length**2****************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之后***index>length****index>length**2****************");

        System.out.println("****************插入之前*******3******************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之前*******3******************");
        singleLinkedList.insertDataToLinkedListByIndex(46,2);
        System.out.println("****************插入之后***插到尾结点位置******3****************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之后***插到尾结点位置******3****************");

        System.out.println("****************插入之前*******4******************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之前*******4******************");
        singleLinkedList.insertDataToLinkedListByIndex(990,1);
        System.out.println("****************插入之后***插到头结点位置******4****************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之后***插到头结点位置******4****************");


        System.out.println("****************插入之前*******5******************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之前*******5******************");
        singleLinkedList.insertDataToLinkedListByIndex(666,3);
        System.out.println("****************插入之后***插到链表中间******5****************");
        singleLinkedList.printLinkList();
        System.out.println("****************插入之后***插到链表中间******5****************");
    }
}



双向链表

双向链表:每个结点不止有一个后继指针next指向后面的结点,还有一个前驱指针prev指向前面的结点
在这里插入图片描述

  • 添加结点
   /**
     * 将结点添加到双向链表中
     *
     * @param newNode 待添加结点
     */
    public void add(DoubleNode newNode) {
        if (head == null) {
            head = new DoubleNode(null);
            head.next = newNode;
            newNode.pre = head;
            return;
        }
        DoubleNode doubleNode = head;
        //找到链表中最后一个结点
        while (doubleNode.next != null) {
            doubleNode = doubleNode.next;
        }
        // 将原链表中最后一个结点的后继指针指向要添加的结点
        doubleNode.next = newNode;
        // 将待添加的结点的前驱指针指向原链表中的最后一个结点
        newNode.pre = doubleNode;
    }

  • 删除结点
 /**
     * 删除双向链表中值为delData的结点
     *
     * @param delData
     */
    public void deleteNode(int delData) {
        DoubleNode doubleNode = head.next;
        if (doubleNode == null) {
            return;
        }
        while (doubleNode != null) {
            // 找到待删除的结点
            if (doubleNode.data == delData) {
                doubleNode.pre.next = doubleNode.next;
                // 尾结点的next指向null,不需要执行以下语句
                if (doubleNode.next != null) {
                    doubleNode.next.pre = doubleNode.pre;
                }
            }
            doubleNode = doubleNode.next;
        }
    }

双向链表完整代码:

  • 结点类
package com.linkedlist.dto;

/**
 * @Author: xpengfei
 * @Date: 2020/6/14 16:01
 * @Description:双向链表结点类
 */
public class DoubleNode {

    /**
     * 节点数据域,用于存储数据
     */
    public Integer data;

    /**
     * 节点的指针域,前驱指针,用于存储上一个节点的位置
     */
    public DoubleNode pre;

    /**
     * 节点的指针域,后继指针,用于存储下一个节点的位置
     */
    public DoubleNode next;


    public DoubleNode(Integer data) {
        this.data = data;
    }

    public DoubleNode(int data, DoubleNode pre, DoubleNode next) {
        this.data = data;
        this.pre = pre;
        this.next = next;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public DoubleNode getPre() {
        return pre;
    }

    public void setPre(DoubleNode pre) {
        this.pre = pre;
    }

    public DoubleNode getNext() {
        return next;
    }

    public void setNext(DoubleNode next) {
        this.next = next;
    }
}

  • 双向链表操作类
package com.linkedlist;

import com.linkedlist.dto.DoubleNode;

/**
 * @Author: xpengfei
 * @Date: 2020/6/6 22:25
 * @Description:双向链表
 */
public class DoubleLinkedList {

    /**
     * 双向链表头结点
     */
    protected DoubleNode head;


    /**
     * 将结点添加到双向链表中
     *
     * @param newNode 待添加结点
     */
    public void add(DoubleNode newNode) {
        if (head == null) {
            head = new DoubleNode(null);
            head.next = newNode;
            newNode.pre = head;
            return;
        }
        DoubleNode doubleNode = head;
        //找到链表中最后一个结点
        while (doubleNode.next != null) {
            doubleNode = doubleNode.next;
        }
        // 将原链表中最后一个结点的后继指针指向要添加的结点
        doubleNode.next = newNode;
        // 将待添加的结点的前驱指针指向原链表中的最后一个结点
        newNode.pre = doubleNode;
    }


    /**
     * 删除双向链表中值为delData的结点
     *
     * @param delData
     */
    public void deleteNode(int delData) {
        DoubleNode doubleNode = head.next;
        if (doubleNode == null) {
            return;
        }
        while (doubleNode != null) {
            // 找到待删除的结点
            if (doubleNode.data == delData) {
                doubleNode.pre.next = doubleNode.next;
                // 尾结点的next指向null,不需要执行以下语句
                if (doubleNode.next != null) {
                    doubleNode.next.pre = doubleNode.pre;
                }
            }
            doubleNode = doubleNode.next;
        }
    }

    /**
     * 打印出链表中的所有节点
     */
    public void printLinkList() {
        System.out.println("--------------------链表输出开始----------------------");
        DoubleNode temp = head;
        while (temp != null) {
            System.out.print(temp.data + "\t");
            temp = temp.next;
        }
        System.out.println();
        System.out.println("--------------------链表输出结束----------------------");
    }

}

  • 双向链表操作测试类
package com.linkedlist;

import com.linkedlist.dto.DoubleNode;
import org.testng.annotations.Test;

/**
 * @Author: xpengfei
 * @Date: 2020/6/14 16:48
 * @Description:双向链表测试类
 */
public class DoubleLinkedListTest {

    /**
     * 测试添加结点
     */
    @Test(priority = 1)
    public void add() {
        DoubleNode node = new DoubleNode(66);
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
        doubleLinkedList.add(node);
        node = new DoubleNode(99);
        doubleLinkedList.add(node);
        node = new DoubleNode(233);
        doubleLinkedList.add(node);
        doubleLinkedList.printLinkList();
    }

    /**
     * 测试删除结点
     */
    @Test(priority = 2)
    public void deleteNode(){
        DoubleNode node = new DoubleNode(66);
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
        doubleLinkedList.add(node);

        node = new DoubleNode(99);
        doubleLinkedList.add(node);

        node = new DoubleNode(233);
        doubleLinkedList.add(node);

        node = new DoubleNode(456);
        doubleLinkedList.add(node);

        node = new DoubleNode(789);
        doubleLinkedList.add(node);

        doubleLinkedList.printLinkList();

        System.out.println("***************删除头结点66*******************");
        doubleLinkedList.deleteNode(66);
        doubleLinkedList.printLinkList();

        System.out.println("***************删除尾结点789*******************");
        doubleLinkedList.deleteNode(789);
        doubleLinkedList.printLinkList();

        System.out.println("***************删除中间结点233*******************");
        doubleLinkedList.deleteNode(233);
        doubleLinkedList.printLinkList();
    }
}

在这里插入图片描述

循环链表

循环链表与单链表的区别在于:单链表的尾结点指针指向空地址,而循环链表的尾结点指针指向链表的头结点。
在这里插入图片描述

  • 优点:从链尾到链头比较方便,适合处理具有环形结构特点的数据。
  • 循环链表添加结点
/**
     * 添加结点
     *
     * @param newNode 待添加结点
     */
    public void addNode(CircleNode newNode) {
        if (head == null) {
            head = new CircleNode(null);
            tail = new CircleNode(null);
            head.setNext(newNode);
            newNode.setNext(tail);
            tail.setNext(head);
        }
        CircleNode circleNode = head;
        // 链表中无数据,只有首尾结点时
        if (circleNode.getNext().getNext() == head) {
            circleNode.setNext(newNode);
            newNode.setNext(tail);
            return;
        }
        //找到尾结点前的一个节点
        while (circleNode.getNext().getNext() != head) {
            circleNode = circleNode.getNext();
        }
        circleNode.setNext(newNode);
        newNode.setNext(tail);
    }

添加结点测试

  • 循环链表删除结点
 /**
     * 删除循环链表中值为delData的结点
     *
     * @param delData
     */
    public void deleteNode(int delData) {
        CircleNode circleNode = head;

        // head结点的下一个结点指向head,链表为空
        if (circleNode.getNext() == head) {
            return;
        }

        // 结点指针域指向head结点,则链表遍历结束
        while (circleNode.getNext().getNext() != head) {
            // 找到待删除数据的上一个结点
            if (circleNode.getNext().getData() == delData) {
                circleNode.setNext(circleNode.getNext().getNext());
                return;
            }
            circleNode = circleNode.getNext();
        }
    }

单向循环链表删除结点测试

总结

在这里插入图片描述
在这里插入图片描述

栈是一种只能在一端进行插入或删除操作的线性表,其中允许进行插入或删除的一端称为栈顶。
在这里插入图片描述

顺序栈

  • 顺序栈实现:
package com.stack;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Array;

/**
 * @Author: xpengfei
 * @Date: 2020/6/25 9:28
 * @Description:数组实现顺序栈
 */
public class ArrayStack<T> {

    private static final Logger logger = LoggerFactory.getLogger(ArrayStack.class);
    /**
     * 存放栈元素的数组
     */
    private T[] arrayStack;

    /**
     * 栈中元素个数
     */
    private int n = 0;

    /**
     * 无参构造方法,初始化栈数组大小为10
     */
    public ArrayStack(Class<T> type) {
        arrayStack = (T[]) Array.newInstance(type, 10);
        logger.info("栈初始容量为:" + 10);
    }

    /**
     * 构造方法,指定栈数组大小
     *
     * @param size
     */
    public ArrayStack(Class<T> type, int size) {
        this.arrayStack = (T[]) Array.newInstance(type, size);
        logger.info("栈初始容量为:" + size);

    }

    /**
     * 调整栈数组的大小
     *
     * @param maxSize
     */
    private void reSize(Class<T> type, int maxSize) {
    	// 通过 Array.newInstance()方法实现Java中数组的泛型
        T[] temp = (T[]) Array.newInstance(type, maxSize);
        for (int i = 0; i < arrayStack.length; i++) {
            temp[i] = arrayStack[i];
        }
        arrayStack = temp;
        logger.info("******************栈已扩容为原来的2倍:" + 2 * n + "*******************");
    }

    /**
     * 判断栈是否为空
     *
     * @return true:空  false:非空
     */
    public boolean isEmpty() {
        return n == 0;
    }

    /**
     * 入栈:向栈顶插入元素
     *
     * @param item
     */
    public void push(Class<T> type, T item) {
        // 栈满时,自动扩容为运来的两倍
        if (n == arrayStack.length) {
            reSize(type, n * 2);
        }
        arrayStack[n] = item;
        //栈元素总数+1
        n++;
    }

    /**
     * 出栈:从栈顶移除元素并将元素返回
     *
     * @return
     */
    public T pop() {
        // 栈空则返回null
        if (isEmpty()) {
            return null;
        }
        T temp = arrayStack[n - 1];
        arrayStack[n - 1] = null;
        // 栈元素个数减一
        n--;
        return temp;
    }


    /**
     * 返回栈大小
     *
     * @return
     */
    public int size() {
        return n;
    }

    /**
     * 输出栈元素
     */
    public void printStack() {
        logger.info("---------------栈元素输出开始----------------");
        if (size() == 0) {
            logger.info("栈空!");
        } else {
            for (int i = 0; i < n; i++) {
                logger.info(arrayStack[i] + "\t");
            }
            logger.info("");
        }
        logger.info("---------------栈元素输出结束----------------");
    }
}

测试出栈过程:
出栈过程

数组实现顺序栈过程中,遇到Java泛型问题,定义数组时使用泛型如private T[] arrayStack;在赋值时会报错java.lang.arraystoreexception,通过Array.newInstance()方法在Java中实现数组的泛型

链式栈

  • 链式栈实现:
package com.stack;

import com.stack.dto.LinkedListNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Author: xpengfei
 * @Date: 2020/6/25 9:28
 * @Description:链表实现链式栈
 */
public class LinkedListStack<T> {

    private static final Logger logger = LoggerFactory.getLogger(LinkedListStack.class);

    /**
     * 头结点
     */
    private LinkedListNode<T> head;
    /**
     * 栈中元素个数
     */
    private int n = 0;

    /**
     * 头插法,入栈
     *
     * @param newData 待插入的数据
     */
    public void push(T newData) {
        // 临时结点指向原链表头结点
        LinkedListNode<T> oldHead = head;
        // 将newData结点赋给头结点head
        head = new LinkedListNode(newData);
        // 将新的头结点指向原头结点
        head.setNext(oldHead);
        // 栈元素+1
        n++;
    }

    /**
     * 栈顶元素出栈
     */
    public T pop() {
        // 栈空,返回null
        if (isEmpty()) {
            return null;
        }
        LinkedListNode<T> tempNode = head;
        // 删除出栈的结点
        head = head.getNext();
        // 栈中元素个数减一
        n--;
        return tempNode == null ? null : tempNode.getData();
    }

    /**
     * 判断栈是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return n == 0;
    }

    /**
     * 返回栈大小(栈中元素个数)
     *
     * @return
     */
    public int size() {
        return n;
    }

    /**
     * 输出链式栈
     */
    public void printLinkedListStack(){
        LinkedListNode<T> tempNode = head;
        logger.info("*****************栈元素输出开始*********************");
        while (tempNode != null) {
            System.out.print(tempNode.getData()+"\t");
            tempNode = tempNode.getNext();
        }
        System.out.println();
        logger.info("*****************栈元素输出结束*********************");
    }
}

队列

在这里插入图片描述

  • 队列的特点是先进先出,生活中队列的例子比比皆是,如:平时排队的场景

链式队列

  • 链式队列实现:
package com.queue.dto;

/**
 * @Author: xpengfei
 * @Date: 2020/6/26 14:50
 * @Description:链式队列节点类
 */
public class LinkedListQueueDto<T> {

    /**
     * 数据域
     */
    private T data;
    /**
     * 链式队列指针域
     */
    private LinkedListQueueDto<T> next;

    public LinkedListQueueDto(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public LinkedListQueueDto<T> getNext() {
        return next;
    }

    public void setNext(LinkedListQueueDto<T> next) {
        this.next = next;
    }
}

package com.queue;

import com.queue.dto.LinkedListQueueDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Author: xpengfei
 * @Date: 2020/6/26 10:25
 * @Description:链式队列
 */
public class LinkedListQueue<T> {

    private static final Logger logger = LoggerFactory.getLogger(LinkedListQueue.class);

    /**
     * 链式队列中结点个数
     */
    private int n = 0;

    /**
     * 链式队列头结点
     */
    private LinkedListQueueDto<T> head;

    /**
     * 链式队列尾结点
     */
    private LinkedListQueueDto<T> rear;


    /**
     * 入队,链表尾部入队
     *
     * @param data 待入队数据
     */
    public void enqueue(T data) {
        LinkedListQueueDto<T> newNode = new LinkedListQueueDto<>(data);
        // 队列为空时,直接在首结点后插入
        if (isEmpty()) {
            head = new LinkedListQueueDto<>(null);
            head.setNext(newNode);
            // 将新节点指向尾结点
            newNode.setNext(rear);
        } else {
            LinkedListQueueDto<T> tempNode = head;
            //找到队列中尾结点前一个节点
            while (tempNode.getNext() != null) {
                tempNode = tempNode.getNext();
            }
            //新节点插入
            tempNode.setNext(newNode);
            // 将新节点指向尾结点
            newNode.setNext(rear);
        }
        // 队列中节点数+1
        n++;
    }

    /**
     * 出队,链式队列队头出队
     *
     * @return
     */
    public T dequeue() {
        if (isEmpty()) {
            return null;
        }
        LinkedListQueueDto<T> tempNode = head.getNext();
        head.setNext(head.getNext().getNext());
        n--;
        return tempNode.getData();
    }

    /**
     * 判断队列是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return n == 0;
    }

    /**
     * 获取队列中数据结点个数
     *
     * @return
     */
    public int size() {
        return n;
    }

    /**
     * 输出链式队列
     */
    public void printLinkedListQueue() {
        LinkedListQueueDto<T> tempNode = head.getNext();
        logger.info("--------------------链式队列输出开始---------------------");
        while (tempNode != null) {
            System.out.print(tempNode.getData() + "\t");
            tempNode = tempNode.getNext();
        }
        System.out.println();
        logger.info("--------------------链式队列输出结束---------------------");
    }
}

顺序队列

  • 顺序队列实现:
package com.queue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Array;

/**
 * @Author: xpengfei
 * @Date: 2020/6/26 16:46
 * @Description:数组实现队列
 */
public class ArrayQueue<T> {

    private static final Logger logger = LoggerFactory.getLogger(ArrayQueue.class);

    /**
     * 数组队列
     */
    private T[] queue;

    /**
     * 数组大小
     */
    private int size;

    /**
     * 数组中最后一个元素的下标,-1:队列为空
     */
    private int rearIndex = -1;

    /**
     * 构造函数初始化数组
     *
     * @param type
     * @param size
     */
    public ArrayQueue(Class<T> type, int size) {
        if (size <= 0) {
            logger.error("数组大小不能小于0!");
            return;
        }
        this.size = size;
        queue = (T[]) Array.newInstance(type, size);
    }


    /**
     * 判断队列是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return rearIndex == -1;
    }

    /**
     * 判断队列是否已满
     *
     * @return
     */
    public boolean isFull() {
        return rearIndex == size - 1;
    }

    /**
     * 入队
     *
     * @param data
     * @return
     */
    public boolean enqueue(T data) {
        if (isFull()) {
            logger.warn("队列已满!" + data + "入队失败");
            return false;
        }
        queue[++rearIndex] = data;
        return true;
    }

    /**
     * 出队
     *
     * @return
     */
    public T dequeue() {
        // 队空
        if (isEmpty()) {
            logger.error("队空!");
            return null;
        } else {
            // 待出队数据
            T temp = queue[0];
            //将数组中数据向前移动一位
            for (int i = 0; i <= rearIndex - 1; i++) {
                queue[i] = queue[i + 1];
            }
            rearIndex--;
            return temp;
        }
    }

    /**
     * 输出数组队列
     */
    public void printQueue() {
        if (isEmpty()) {
            logger.info("队列为空!");
            return;
        }
        logger.info("---------------------数组队列输出开始--------------------");
        for (int i = 0; i <= rearIndex; i++) {
            System.out.print(queue[i] + "\t");
        }
        System.out.println();
        logger.info("---------------------数组队列输出结束--------------------");
    }

}

ps:文章仅作记录,水平有限,如有错误之处,欢迎交流指正~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值