双向链表及单向循环链表

双向链表

结构

static class ListNode{
        private int val;//值
        private ListNode prev;//前驱
        private ListNode next;//后继

        public ListNode(int val){
            this.val=val;
        }
    }

public ListNode head;//双向链表的头节点
public ListNode last;//双向链表的尾节点

基本操作

头部插入

public void insertAtHead(int val) {
    // 创建一个新的节点
    ListNode newNode = new ListNode(val);

    // 如果链表为空,即 last 也是头节点
    if (head.next == null) {
        head.next = newNode;
        newNode.prev = head;
        newNode.next = null;
        last = newNode;
    } else {
        // 将新节点的后继指针指向头节点的后继节点
        newNode.next = head.next;
        // 将新节点的前驱指针指向头节点
        newNode.prev = head;

        // 将头节点的后继节点的前驱指针指向新节点
        head.next.prev = newNode;
        // 将头节点的后继指针指向新节点
        head.next = newNode;
    }
}

头部删除

public void deleteAtHead() {
    // 检查链表是否为空(即头节点的下一个节点为 null)
    if (head.next == null) {
        System.out.println("List is empty. Nothing to delete at head.");
        return;
    }

    // 获取要删除的节点,即头节点之后的第一个数据节点
    ListNode nodeToDelete = head.next;

    // 将头节点的 next 指向要删除节点的 next
    head.next = nodeToDelete.next;

    // 判断要删除的节点是否是最后一个数据节点
    if (nodeToDelete.next == last) {
        // 如果要删除的节点是最后一个数据节点,将 last 指向头节点
        last.prev = head;
    } else {
        // 否则,将要删除的节点的 next 的 prev 指针指向头节点
        nodeToDelete.next.prev = head;
    }

    // 清除要删除的节点的前后链接
    nodeToDelete.prev = null;
    nodeToDelete.next = null;
}

尾部插入

public void insertAtTail(int val) {
    // 创建一个新的数据节点
    ListNode newNode = new ListNode(val);
    
    // 获取链表中最后一个数据节点
    ListNode lastDataNode = last.prev;
    
    // 将新节点的 prev 指针指向最后一个数据节点
    newNode.prev = lastDataNode;
    
    // 将新节点的 next 指针指向尾节点
    newNode.next = last;
    
    // 如果链表为空,即 lastDataNode 为 null
    if (lastDataNode == null) {
        // 链表是空的
        head.next = newNode;  // 将头节点的 next 指向新节点
    } else {
        // 链表不为空
        lastDataNode.next = newNode;  // 将最后一个数据节点的 next 指向新节点
    }
    
    // 将尾节点的 prev 指针指向新节点
    last.prev = newNode;
}

尾部删除

public void deleteAtTail() {
    // 检查链表是否为空
    if (last.prev == null) {
        System.out.println("List is empty. Nothing to delete at tail.");
        return;
    }

    // 获取要删除的最后一个数据节点
    ListNode nodeToDelete = last.prev;

    // 将尾节点的 prev 指向要删除节点的前驱节点
    last.prev = nodeToDelete.prev;

    // 判断要删除节点的前驱是否存在
    if (nodeToDelete.prev != null) {
        // 如果前驱节点存在,将前驱节点的 next 指向尾节点
        nodeToDelete.prev.next = last;
    } else {
        // 如果前驱节点不存在,说明链表只剩一个节点,更新 head.next
        head.next = last;
    }

    // 清除要删除节点的前后链接
    nodeToDelete.prev = null;
    nodeToDelete.next = null;
}

指定位置插入

public void insertAtPosition(int val, int position) {
    // 检查位置是否为有效位置(从1开始)
    if (position < 1) {
        System.out.println("Invalid position. Position should be >= 1.");
        return;
    }

    // 创建一个新的数据节点
    ListNode newNode = new ListNode(val);

    // 从头节点开始进行遍历,找到插入位置
    ListNode current = head;
    int currentIndex = 0;

    while (currentIndex < position - 1 && current.next != last) {
        current = current.next;
        currentIndex++;
    }

    // 检查是否已经达到指定位置
    if (currentIndex != position - 1) {
        System.out.println("Position out of bounds. List has fewer than " + position + " elements.");
        return;
    }

    // 将新节点插入在当前位置
    // 新节点的 prev 指向 current
    newNode.prev = current;

    // 新节点的 next 指向 current 的 next
    newNode.next = current.next;

    // 将 current 的 next.prev 指向新节点
    if (current.next != last) {
        current.next.prev = newNode;
    } else {
        // 如果插入在尾节点之前,更新 last 的 prev 指向新节点
        last.prev = newNode;
    }

    // 将 current 的 next 指向新节点
    current.next = newNode;
}

指定位置删除 

public void deleteAtPosition(int position) {
    // 检查位置是否为有效位置(从 1 开始)
    if (position < 1) {
        System.out.println("Invalid position. Position should be >= 1.");
        return;
    }

    // 从头节点开始遍历,找到要删除的位置
    ListNode current = head.next; // 从第一个数据节点开始
    int currentIndex = 0;

    while (currentIndex - 1 < position && current != last) {
        current = current.next;
        currentIndex++;
    }

    // 检查是否找到要删除的位置
    if (current == last || currentIndex != position) {
        System.out.println("Position out of bounds. List has fewer than " + position + " elements.");
        return;
    }

    // 删除指定位置的节点
    ListNode prevNode = current.prev;
    ListNode nextNode = current.next;

    // 将 prevNode 的 next 指向 nextNode
    prevNode.next = nextNode;

    // 将 nextNode 的 prev 指向 prevNode
    if (nextNode != last) {
        nextNode.prev = prevNode;
    } else {
        // 如果删除的是最后一个数据节点,更新 last.prev
        last.prev = prevNode;
    }

    // 清除要删除节点的前后链接
    current.prev = null;
    current.next = null;
}

查找指定数据

public ListNode findNode(int targetValue) {
    // 从头节点的下一个节点(第一个数据节点)开始遍历链表
    ListNode current = head.next;

    // 遍历链表,直到尾节点或找到目标值
    while (current != last) {
        // 检查当前节点的值是否与目标值匹配
        if (current.val == targetValue) {
            return current; // 找到匹配的节点,返回它
        }
        // 移动到下一个节点
        current = current.next;
    }

    // 遍历完成未找到目标值,返回 null
    return null;
}

 计算链表大小

public int size() {
    // 从头节点的下一个节点(第一个数据节点)开始遍历链表
    ListNode current = head.next;
    int count = 0;

    // 遍历链表,直到尾节点
    while (current != last) {
        // 增加计数器
        count++;
        // 移动到下一个节点
        current = current.next;
    }

    // 返回计数器的值,即链表的大小
    return count;
}

 销毁链表

public void destroyList() {
    // 从头节点的下一个节点开始遍历链表
    ListNode current = head.next;

    // 遍历链表,直到尾节点
    while (current != last) {
        // 获取当前节点的下一个节点
        ListNode nextNode = current.next;

        // 清除当前节点的前后链接
        current.prev = null;
        current.next = null;

        // 可选:帮助垃圾回收
        current = null;

        // 移动到下一个节点
        current = nextNode;
    }

    // 重置链表的头节点和尾节点
    head.next = last;  // 将头节点的下一个节点指向尾节点
    last.prev = head;  // 将尾节点的前一个节点指向头节点
}

单向环形链表

应用场景

        Josephu(约瑟夫、约瑟夫环)问题

        设编号为1,2,...,n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列位置,由此产生一个出对编号的序列。

代码实现


public class JosephusProblem {

    public static int solve(int n, int k) {
        // 检查输入参数的有效性
        if (n <= 0 || k <= 0) {
            throw new IllegalArgumentException("Both n and k must be greater than 0.");
        }

        // 创建循环单向链表
        CircularLinkedList list = new CircularLinkedList();

        // 将所有添加到循环单向链表中
        for (int i = 1; i <= n; i++) {
            list.add(i);
        }

        // 获取链表的头节点
        CircularLinkedList.ListNode current = list.getHead();
        CircularLinkedList.ListNode prevNode = list.getHead();

        // 初始化 prevNode
        while (prevNode.next != current) {
            prevNode = prevNode.next;
        }

        // 模拟约瑟夫环问题
        while (current.next != current) {
            // 移动到要删除节点的前一个节点
            for (int i = 1; i < k; i++) {
                prevNode = current;
                current = current.next;
            }

            // 删除当前节点
            System.out.println("Removing node: " + current.val);
            current = list.remove(prevNode, current);
        }

        // 返回剩下的最后一个节点的值
        return current.val;
    }

    public static void main(String[] args) {
        int n = 7;  // 总数
        int k = 3;  // 每隔 k 个删除

        int lastRemaining = solve(n, k);
        System.out.println("Last remaining person at position: " + lastRemaining);
    }
}


class CircularLinkedList {
    // 内部类,表示链表节点
    static class ListNode {
        int val; // 节点的值
        ListNode next; // 下一个节点的指针

        public ListNode(int val) {
            this.val = val;
        }
    }

    private ListNode head; // 链表的头节点
    private ListNode tail; // 链表的尾节点

    // 构造一个空的循环单向链表
    public CircularLinkedList() {
        head = null;
        tail = null;
    }

    // 向链表添加节点
    public void add(int val) {
        ListNode newNode = new ListNode(val);
        if (head == null) {
            head = newNode;
            tail = newNode;
            tail.next = head; // 形成循环
        } else {
            tail.next = newNode;
            tail = newNode;
            tail.next = head; // 形成循环
        }
    }

    // 返回链表的头节点
    public ListNode getHead() {
        return head;
    }

    // 删除节点并返回删除后的节点
    public ListNode remove(ListNode prevNode, ListNode currentNode) {
        // 如果要删除的是链表的头节点
        if (currentNode == head) {
            head = head.next;
            tail.next = head; // 更新循环
        }

        // 更新前一个节点的下一个节点
        prevNode.next = currentNode.next;

        // 如果要删除的是链表的尾节点
        if (currentNode == tail) {
            tail = prevNode; // 更新尾节点
        }

        // 清除当前节点的 next 指针
        currentNode.next = null;

        return prevNode.next;
    }
}

        

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值