数据结构基础2

1.单链表的数据结构+案例
2.双链表的数据结构+案例
3.栈的数据结构(双向链表+数组实现) + 案例
4.队列的数据结构(双向链表+数组实现)
5.写一个NodeUtils工具类
6.全部的代码+测试用例gitee仓库自取


1.单链表的数据结构和案例

单向链表

/**
 * 构造一个单向链表
 */
public class  Node <T>  {
    // 该节点的值 value
    public T value;
    // 该节点指向的下一节点
    public Node nextNode;
    // 构造函数
    public Node(T value){
        this.value = value;
    }
}

案例1:给定一个链表,如何反转链表?

/**
 * @author Tara
 * @implNote 给定一个单向链表,如何反转链表?
 */
public class Pra1 {

    public static void main(String[] args) {
        Object[] arr = {2, 4, 7, 9, 23, 21};
        // 构造一个链表
        Node node = NodeUtils.constructNode(arr);
        // 打印链表轨迹
        NodeUtils.printSingleNode(node);
        // 反转链表
        Node node1 = reverseLinkedList(node);
        //打印链表结构
        NodeUtils.printSingleNode(node1);
    }

    //反转链表
    public static Node reverseLinkedList(Node head){
        Node preNode = null;
        Node nextNode = null;
        while (null!=head){
            nextNode = head.nextNode;
            head.nextNode = preNode;
            preNode = head;
            head = nextNode;
        }
        while (null!=head){
            System.out.print(head.value+" -> ");
        }
        return preNode;
    }
}

案例2:把给定的node节点中有指定值的节点全部移除

/**
 * @author Tara
 * @implNote 给定一个链表,删除所有节点值为x的节点,并返回头节点
 */
public class Pra2 {

    public static void main(String[] args) {
        Object[] arr = {3, 2, 4, 7, 9, 3, 3, 2, 3, 23, 21};
        // 构造一个链表
        Node node = NodeUtils.constructNode(arr);
        // 打印链表轨迹
        NodeUtils.printSingleNode(node);
        // 移除所有值为3的节点
        Node node1 = removeValues(node,2);
        //打印链表结构
        NodeUtils.printSingleNode(node1);
    }

    //移除单向链表的值为xx的节点
    private static Node removeValues(Node head, Object value) {
        // 1.考虑head第一位是否存在要移除的节点
        while (null != head) {
            // 先排除head位置存在要移除的节点
            if (head.value != value) {
                // 首位没有符合条件的就停止。
                break;
            }
            head = head.nextNode;
        }
        // 2.考虑 中间是否存在要移除的节点,存在就跳过去,不存在指针指向下一个
        // 游标指向当前节点
        Node cur = head;
        Node pre = null;
        while (null != cur) {
            if (cur.value == value) {
                pre.nextNode = cur.nextNode;
            } else {
                pre = cur;
            }
            cur = cur.nextNode;
        }
        return head;
    }
}

2.双链表的数据结构和案例

双向链表

/**
 * 构造一个双向链表
 * @param <T>
 */
public class DoubleNode <T> {
    //该节点的值
    public T value;
    // 该节点指向的上一个节点
    public DoubleNode lastNode;
    // 该节点指向的下一节点
    public DoubleNode nextNode;
    // 双向链表的构造方法
    public DoubleNode(T value){
        this.value = value;
    }
}

案例1:给定一个双向链表,如何反转双向链表?

/**
 * @author Tara
 * @implNote 给定一个双向链表,如何反转双向链表?
 */
public class Pra3 {

    public static void main(String[] args) {
        Object[] arr = {2, 4, 7, 9, 23, 21, 8 , 43};
        // 构造一个双向链表
        DoubleNode node = NodeUtils.constructDoubleNode(arr);
        // 打印链表轨迹
        NodeUtils.printDoubleNode(node);
        //打印链表结构
        NodeUtils.printDoubleNode2(node);

        System.out.println("=====================反转双向链表======================");
        DoubleNode node1 = reverseDoubleNodeList(node);
        NodeUtils.printDoubleNode(node1);
        NodeUtils.printDoubleNode2(node1);
    }

    //反转双向链表
    public static DoubleNode reverseDoubleNodeList(DoubleNode head){
        DoubleNode preNode = null;
        DoubleNode nextNode = null;
        while (null!=head){
            nextNode = head.nextNode;
            head.nextNode = preNode;
            head.lastNode = nextNode;
            preNode = head;
            head = nextNode;
        }
        return preNode;
    }
}

案例2:给定一个双向链表,删除所有节点值为x的节点,并返回头节点head

/**
* @author Tara
* @implNote 给定一个双向链表,删除所有节点值为x的节点,并返回头节点head
*/
public class Pra4 {

   public static void main(String[] args) {
       Object[] arr = {3, 2, 4, 7, 9, 3, 3, 2, 3, 23, 21};
       // 构造一个链表
       DoubleNode node = NodeUtils.constructDoubleNode(arr);
       // 打印链表轨迹
       NodeUtils.printDoubleNode(node);
       NodeUtils.printDoubleNode2(node);
       // 移除所有值为3的节点
       DoubleNode removedNode = removeValues(node, 2);
       //打印链表结构
       NodeUtils.printDoubleNode(removedNode);
       NodeUtils.printDoubleNode2(removedNode);
   }

   /**
    * 移除单向链表的值为xx的节点
    * @param head 双链表的一个头部
    * @param value 要移除节点的值value;
    * @return 新头部节点newHead
    */
   private static DoubleNode removeValues(DoubleNode head, Object value) {
       // 1.考虑head第一位是否存在要移除的节点
       while (null != head) {
           // 先排除head位置存在要移除的节点
           if (head.value != value) {
               // 首位没有符合条件的就停止。
               break;
           }
           head = head.nextNode;
           //指针后移后,上一个指针置空;
           head.lastNode = null;
       }
       // 2.考虑 中间是否存在要移除的节点,存在就跳过去,不存在指针指向下一个
       // 游标指向当前节点
       DoubleNode cur = head;
       DoubleNode pre = null;
       DoubleNode next = null;
       while (null != cur) {
           // 记录指针的下一节点
           next = cur.nextNode;
           if (cur.value == value) {
               // 直接将当前节点指向下一节点,跳过cur;
               pre.nextNode = next;
               // 释放当前节点的指向
               cur.nextNode = null;
               cur.lastNode = null;
               // 让下一个节点往回指;
               next.lastNode = pre;
           } else {
               // 当前节点变为下一次的pre节点
               pre = cur;
           }
           //游标后移
           cur = next;
       }
       return head;
   }
}

3.栈的数据结构(双向链表+数组实现) + 案例

双向链表实现

/**
 * 使用双向队列实现栈
 * @param <T>
 */
public class Stack<T> {
    private DoubleEndsQueue<T> queue;
    // 构造方法
    public Stack(){
        queue = new DoubleEndsQueue<>();
    }

    /**
     * 压栈操作
     * @param value
     */
    public void push(T value){
        queue.addFromHead(value);
    }
    // 取值
    public T pop(){
        return queue.popFromHead();
    }

    /**
     * 判断栈是否为空
     * @return
     */
    public boolean isEmpty(){
        return queue.isEmpty();
    }
}

数组实现

/**
 * 使用数组实现固定长度的栈
 */
public class Stack {
    // 存值的数组;
    private Object[] arr;
    // 栈顶部的索引;
    private int index;
    //初始化长度;
    private final int limit;

    // 构造方法初始化
    public Stack(int limit) {
        this.arr = new Object[limit];
        this.index = 0;
        this.limit = limit;
    }

    // 压栈操作
    public void push(Object value) {
        if (index == limit) {
            throw new RuntimeException("栈已经满了,不能再装了");
        }
        this.arr[index] = value;
        index++;
    }

    // 取栈顶元素;
    public Object pop() {
        if (index == 0) {
            throw new RuntimeException("栈已经空了,不能再取了");
        }
        index--;
        Object temp = this.arr[index];
        this.arr[index] = null;
        return temp;
    }

    // 栈为空判断
    public boolean isEmpty() {
        return index == 0;
    }

    @Override
    public String toString() {
        String str = "";
        for (int i = 0; i < arr.length; i++) {
            str += arr[i] + ",";
        }
        return str;
    }
}

案例1:设计一个栈StackMin来实现,向栈中加入若干个值,以最高的效率取出栈中的最小值,设计出O(l)的方案

/**
 * @Author:      Tara.Li(李春侣)
 * @Contact Me WeChat: flyme9988
 * @ReWroteBy:
 * @Date:  2021-12-19 20:23:08
 * @Description:  设计一个栈,无论怎么压栈,弹出,都能以最小的时间复杂度,获取栈中的最小元素。
 */
public class StackMin {
    //定义一个正常存储的栈
    private Stack<Integer> stackData;
    // 定义一个存储最小元素的栈,
    private Stack<Integer> stackMin;

    //构造函数

    public StackMin() {
        stackData = new Stack<>();
        stackMin = new Stack<>();
    }
    /**
     * 压栈
     * @param newNum 新压入栈的值
     */
    public void push(int newNum){
        //如果记录最小值的栈为空,,即为第一个元素,直接两个栈都进。
        if (this.stackMin.isEmpty()){
            stackMin.push(newNum);

            // 如果新压入的元素小于stackMin元素的栈顶元素,就压入栈顶。
        }else if (newNum < this.stackMin.peek()) {
            stackMin.push(newNum);

            // 如果新压入的元素大于等于stackMin元素,就再压一次栈顶元素。
        }else if (newNum >= this.stackMin.peek()){
            Integer temp = this.stackMin.peek();
            this.stackMin.push(temp);
        }

        // 无论stackMin怎么变动,stackData都是正常push值的
        stackData.push(newNum);
    }

    /**
     * 弹出按照正常情况弹出stackData栈的数据
     * @return
     */
    public Object pop(){
        //判断栈是不是为空
        if (stackData.isEmpty()){
            throw new RuntimeException("当前栈元素已经空了");
        }
        // 两个同步弹出,保证栈内元素一致。
        stackMin.pop();
        return  stackData.pop();
    }

    /**
     * 获取最小元素
     * @return
     */
    public Object getMin(){
        //判断栈是不是为空
        if (stackData.isEmpty()){
            throw new RuntimeException("当前栈元素已经空了");
        }
        //当前栈顶永远存储的是最小元素。
        return this.stackMin.peek();
    }
}

测试案例1

/**
 * @author Tara
 * @implNote  取任意一个栈,栈中的最小元素,设计出O(l)的方案
 */
public class Pra9 {
    public static void main(String[] args) {

        StackMin stack = new StackMin();

        // 想栈中加入最小元素
        stack.push(8);
        stack.push(2);
        stack.push(3);
        stack.push(2);
        stack.push(3);
        stack.push(5);
        stack.push(7);
        stack.push(3);
        stack.push(99);
        stack.push(0);
        stack.push(12);
        stack.push(17);
        stack.push(19);
        stack.push(13);

        System.out.println("最小元素为:"+stack.getMin());
        // 当前栈中的元素为:
        for (int i = 0; i < 15; i++) {
            System.out.print(stack.pop()+",");
        }
    }
}

4.队列的数据结构(双向链表+数组实现)

双向链表实现

/**
 * 使用双向链表实现队列
 * @param <T>
 */
public class Queue<T> {
    // 初始化一个双向链表
    private DoubleEndsQueue<T> queue;
    public Queue(){
        queue = new DoubleEndsQueue<>();
    }

    /**
     * 放进队列
     * @param value
     */
    public void push(T value){
        queue.addFromHead(value);
    }

    /**
     * 从队列中取出
     * @return
     */
    public T poll(){
        return queue.popFromBottom();
    }

    /**
     * 判断是否为空
     * @return
     */
    public boolean isEmpty(){
        return queue.isEmpty();
    }
}

数组实现

/**
 * 使用数组实现固定 长度的队列
 */
public class Queue {
    // 数组实现
    private Object[] arr;
    // 添加元素位置标记
    private int pushi;
    // 弹出元素位置标记
    private int polli;
    // 用于记录当前占用了几个空间,最大空间是limit
    private int size;
    // 队列大小
    private final int limit;

    //构造方法
    public Queue(int limit) {
        this.arr = new Object[limit];
        pushi = 0;
        polli = 0;
        size = 0;
        this.limit = limit;
    }

    //队列中添加值value
    public void push(Object value) {
        if (size == limit) {
            throw new RuntimeException("队列满了,不能再push了");
        }
        // 占用空间+1;
        size++;
        // 当前位置指针存储值
        arr[pushi] = value;
        // 前置指针往下一个位置指;
        pushi = nextIndex(pushi);
    }
    
    //取出一个队列中的值
    public Object pop() {
        if (size == 0) {
            throw new RuntimeException("队列空了,不能再poll了");
        }
        //占用空间--
        size--;
        Object temp = arr[polli];
        arr[polli] = 0; // 回收,值置空
        polli = nextIndex(polli);
        return temp;
    }

    /**
     * 获取下一个位置索引
     *
     * @param i 遇到最大位置,直接跳到0,实现循环复用
     * @return
     */
    private int nextIndex(int i) {
        // limit-1是最后一个元素 ,如果到了最后一个元素,下一个就返回到0,否则,继续往下索引;
        return i < limit - 1 ? i + 1 : 0;
    }

    // 队列是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public String toString() {
        String str = "";
        for (int i = 0; i < arr.length; i++) {
            str += arr[i].toString() + ",";
        }
        return str;
    }
}


结尾:部分工具类代码

双向链表写的队列方法DoubleEndsQueue

/**
 * 定义一个双头链表队列,可以从头部插入,头部移除,尾部插入,尾部移除
 *
 * @param <T>
 */
public class DoubleEndsQueue<T> {
    //用双向链表 定义 头部,和尾部指针
    public DoubleNode head;
    public DoubleNode tail;

    /**
     * 从头部向队列中添加值
     *
     * @param value
     */
    public void addFromHead(T value) {
        // 创建一个节点存储传进来的value
        DoubleNode cur = new DoubleNode(value);
        // 如果当前队列为空(没有节点),则头,尾都指向这个新节点cur
        if (null == head) {
            head = cur;
            tail = cur;
        } else {//节点不为空,往头部添加,head前移,尾部不动
            cur.nextNode = head;
            head.lastNode = cur;
            head = cur;
        }
    }

    /**
     * 从尾部向队列中添加值
     *
     * @param value
     */
    public void addFromBottom(T value) {
        DoubleNode<T> cur = new DoubleNode(value);
        if (null == head) {
            head = cur;
            tail = cur;
        } else {
            cur.lastNode = tail;
            tail.nextNode = cur;
            tail = cur;
        }
    }

    /**
     * 从头部取值
     *
     * @return
     */
    public T popFromHead() {
        if (null == head) {
            return null;
        }
        DoubleNode<T> cur = head;
        // 头部和尾部指向同一个节点==>只有一个节点
        if (head == tail) {
            head = null;
            tail = null;
        } else { // 有两个及以上节点
            head = head.nextNode;
            head.lastNode = null;
            cur.nextNode = null;
        }
        return cur.value;
    }

    /**
     * 从尾部取值
     *
     * @return
     */
    public T popFromBottom() {
        if (null == head) {
            return null;
        }
        DoubleNode<T> cur = tail;
        // 只有一个元素
        if (head == tail) {
            head = null;
            tail = null;
        } else { // 有两个及以上元素
            tail = tail.lastNode;
            tail.nextNode = null;
            cur.lastNode = null;
        }
        // 返回底部元素;
        return cur.value;
    }

    // 判断栈是否为空
    public boolean isEmpty() {
        return head == null;
    }
}

6.全部的代码+测试用例gitee仓库自取

https://gitee.com/flyme9988/leetcode

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 二,顺序表 1,顺序表的形式。(先忘掉python的列表这些数据结构) 内存:以一个字节为索引单位;类型本质:in...
    xian_yu阅读 213评论 0 1
  • 链表:数据存储结构我们通过一个简单的场景,了解一下链表的数据存储结构。那就是LRU缓存淘汰算法。 缓存是一种提高数...
    初心myp阅读 618评论 0 1
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,646评论 0 13
  • 概况 学好算法和数据结构对培养编程内力很重要 3Points: Chunk it up Deliberate pr...
    番茄沙司a阅读 1,881评论 0 3
  • 1.链表的存储结构 相比数组,链表是一种稍微复杂一点的数据结构。对于初学者来说,掌握起来也要比数组稍难一些。这两个...
    青漾阅读 284评论 0 0