数据结构四、实现链表(链表为底层的动态栈and队列)与递归(猴子吃peach)以及单链表的递归

  • 数组,栈,队列底层都是静态数组
    • 靠resize解决固定容量的问题
  • 链表是真正的动态数据结构

链表描述

  • 链表
    • 数据存在节点(node)中,(Node next)指向下一个节点的引用
    • 优点:
      • 不需要处理固定容量的问题,真正的动态
      • 增删快
    • 缺点:
      • 丧失了随机访问的能力
      • 查询慢,没有索引,需要逐个查询

创建链表

1.创建一个动态链表了
public class MyLinked<T> {
   
}
2.创建一个节点(内部类)
  • 在链表中,每一个节点都有一个指针和一个数据
  • 顺便重写节点toString,方便后续调用
//链表中数据的数据结构(节点)
    private class Node {
   
        T ele;
        Node next; //指向下一个节点

        //构造方法
        public Node(T ele) {
   
            this.ele = ele;
            next = null;
        }

        public Node() {
   
            this(null);
        }

        //重写Node toString  ele是int属性的不影响,若是对象,就直接调用toString方法了
        @Override
        public String toString() {
   
            return ele.toString();
        }
    }
3.初始化链表
  • size表示链表存储元素
  • 定义链表的虚拟头结点
    • 这个相当于null,实际不存在
    • 虚拟头节点是解决在头部添加节点的特殊处理
    • 若没有虚拟头节点,一定要注意链表为空的情况
  • 链表的构造方法
    • 创建一个长度为空的链表,虚拟头节点为空
 //链表中存放元素的个数
    private int size;
    //链表的头 虚拟头节点(解决添加头结点问题)
    private Node dummyHead;

    //链表的数据结构
    public MyLinked() {
   
        size = 0;
        dummyHead = new Node(null);
    }

    //获取链表中元素的个数
    public int getSize() {
   
        return size;
    }

    //判断链表是否为空
    public boolean isEmpty() {
   
        return size == 0;
    }
4.链表的增
  • 引用上述的头节点来解决添加头部元素问题,
    • 若没有头结点,则遍历为0的位置添加头节点,只能添加到第二个位置,无法绑定,
  • 在任意位置添加节点,
    • 引入指针pre,重点也是找到待添加元素的前一个节点 pre
    • 第一步先判断链表是否为空
    • 创建一个新节点node,将待插入的值存入节点内
    • 将指针pre指向虚拟头节点
    • 然后开始遍历,找到待插入的节点的前一个节点(头节点)
    • node.next = pre.next;
      pre.next = node;
    • 新节点的后继指向待插入节点的头节点的后继
    • 待插入节点的头节点的后继指向新节点
    • 注:
      • for (int i = 0; i < index; i++) {
        pre = pre.next;
        }
      • 这里必须是小于index,只有小于 index才能确定是待插入节点的头节点
      • node.next = pre.next;
        pre.next = node;
      • 顺序不能乱
      • 顺序乱了,就变成待插入节点的头节点的后继指向新节点,新节点的后继指向自己了,就无法添加成功
//链表的头部增加节点
    public void addHead(T ele) {
   
        add(ele, 0);
    }

    //在任意位置添加节点 重点是找到待添加元素的前一个节点 pre
    public void add(T ele, int index) {
   
        if (index < 0 || index > size) {
   
            throw new IllegalArgumentException("index is error!!");
        }
        //创建一个新节点
        Node node = new Node(ele);
        Node pre = dummyHead;//待插入位置的前一个节点
        //找到待插入节点的位置
        for (int i = 0; i < index; i++) {
   
            pre = pre.next;
        }
        node.next = pre.next;
        pre.next = node;
        size++;
    }

    //在尾部添加节点
    public void addTail(T ele) {
   
        add(ele, size);
    }
5.链表遍历
  • 实际就是输出,重写toString
  • 创建一个新节点cur ,让他指向虚拟节点的后继(可以理解为是索引为0的节点)
  • 在开始进行循环,如果cur节点的后继不是空,就一直添加,为空则跳出循环
//链表的遍历 也就是打印
    @Override
    public String toString() {
   
        StringBuilder result = new StringBuilder();
        result.append("HEAD ");
        //从头节点开始遍历
        Node cur = dummyHead.next;//当前节点
        while (cur != null) {
   
            result.append<
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
循环链表是一种特殊的链表结构,它的尾节点指向头节点,形成一个环形结构。与单链表相比,循环链表的插入和删除操作更为方便,因为不需要考虑头节点的特殊情况。 设计与实现循环链表的关键是定义节点类和循环链表类。节点类需要包含一个数据成员,用来保存节点数据,以及一个指针成员,用来指向下一个节点。循环链表类则需要包含头节点指针链表长度等属性,以及插入、删除和打印链表等方。 首先,定义节点类: class ListNode { public: int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; 然后,定义循环链表类: class CircularLinkedList { private: ListNode *head; int count; public: CircularLinkedList() { head = NULL; count = 0; } // 在链表末尾插入节点 void insertAtEnd(int val) { ListNode *newNode = new ListNode(val); if (head == NULL) { head = newNode; head->next = head; } else { ListNode *temp = head; while (temp->next != head) { temp = temp->next; } temp->next = newNode; newNode->next = head; } count++; } // 删除指定位置的节点 void deleteAt(int pos) { if (pos < 1 || pos > count) { cout << "位置不合" << endl; return; } ListNode *temp = head; if (pos == 1) { head = head->next; delete temp; } else { for (int i = 1; i < pos - 1; i++) { temp = temp->next; } ListNode *nodeToDelete = temp->next; temp->next = temp->next->next; delete nodeToDelete; } count--; } // 打印链表 void printLinkedList() { if (head == NULL) { cout << "链表为空" << endl; return; } ListNode *temp = head; do { cout << temp->val << " "; temp = temp->next; } while (temp != head); cout << endl; } }; 通过定义节点类和循环链表类,我们可以方便地进行循环链表的插入、删除和打印操作。在闯关过程中,我们可以根据题目要求,使用循环链表来解决相关问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值