IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 链表经典算法题 -> 正文阅读

[数据结构与算法]链表经典算法题

链表经典算法题

1.快慢指针

1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点

class Node{
    public int value;
    public Node next;

public Node(int value) {
    this.value = value;
    next = null;
} 
public static Node midOrUpMidNode(Node head) {
    if (head == null || head.next == null || head.next.next == null) {
        return head;
    }
    Node fast = head;
    Node slow = head;
    while (fast.next != null && fast.next.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    return slow;
}

2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点

public static Node midOrDownMidNode(Node head) {
    if (head == null || head.next == null || head.next.next == null) {
        return head;
    }
    Node fast = head;
    Node slow = head;
    while (fast.next != null && fast.next.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    return fast.next == null ? slow : slow.next;
}

3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个

public static Node midOrUpMidPreNode(Node head) {
    if (head == null || head.next == null || head.next.next == null) {
        return head;
    }
    Node fast = head;
    Node slow = head;
    Node cur = null;
    while (fast.next != null && fast.next.next != null) {
        fast = fast.next.next;
        cur = slow;
        slow = slow.next;
    }
    return cur;
}

4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个

public static Node midOrDownMidPreNode(Node head) {
    if (head == null || head.next == null || head.next.next == null) {
        return head;
    }
    Node fast = head;
    Node slow = head;
    Node cur = null;
    while (fast.next != null && fast.next.next != null) {
        fast = fast.next.next;
        cur = slow;
        slow = slow.next;
    }
    return fast.next == null ? cur : slow;
}

2.给定一个单链表的头节点head,请判断该链表是否为回文结构

1)栈方法特别简单(笔试用)

public static boolean isPalindrome1(Node head) {
    if (head == null || head.next == null) {
        return true;
    }
    Stack<Node> stack = new Stack<>();
    Node node = head;
    while (node != null) {
        stack.push(node);
        node = node.next;
    }
    node = head;
    while (!stack.isEmpty()) {
        if (node.value != stack.pop().value) {
            return false;
        }
        node = node.next;
    }
    return true;
}

public static boolean isPalindrome2(Node head) {
    if (head == null || head.next == null) {
        return true;
    }
    Node right = head.next;
    Node cur = head;
    while (cur.next != null && cur.next.next != null) {
        right = right.next;
        cur = cur.next.next;
    }
    Stack<Node> stack = new Stack<Node>();
    while (right != null) {
        stack.push(right);
        right = right.next;
    }
    while (!stack.isEmpty()) {
        if (head.value != stack.pop().value) {
            return false;
        }
        head = head.next;
    }
    return true;
}

2)改原链表的方法就需要注意边界了(面试用)

public static boolean isPalindrome3(Node head) {
    if (head == null || head.next == null) {
        return true;
    }
    Node fast = head;
    Node slow = head;
    while (fast.next != null && fast.next.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    Node pre = null;
    Node curr = slow.next;
    slow.next = null;
    Node next = null;
    while (curr != null) {
        next = curr.next;
        curr.next = pre;
        pre = curr;
        curr = next;
    }
    Node node = pre;
    Node n1 = head;
    while (n1 != null && pre != null) {
        if (n1.value != pre.value) {
            return false;
        }
        n1 = n1.next;
        pre = pre.next;
    }
    pre = null;
    curr = node;
    next = null;
    while (curr != null) {
        next = curr.next;
        curr.next = pre;
        pre = curr;
        curr = next;
    }
    return true;
}

3.将单向链表按某值划分成左边小、中间相等、右边大的形式

1)把链表放入数组里,在数组上做partition(笔试用)

public static Node listPartition1(Node head, int pivot) {
    if (head == null || head.next == null) {
        return head;
    }
    Node cur = head;
    int i = 0;
    while (cur != null) {
        i++;
        cur = cur.next;
    }
    Node[] nodeArr = new Node[i];
    i = 0;
    cur = head;
    for (i = 0; i != nodeArr.length; i++) {
        nodeArr[i] = cur;
        cur = cur.next;
    }
    arrPartition(nodeArr, pivot);
    for (i = 1; i != nodeArr.length; i++) {
        nodeArr[i - 1].next = nodeArr[i];
    }
    nodeArr[i - 1].next = null;
    return nodeArr[0];
}

public static void arrPartition(Node[] nodeArr, int pivot) {
    int small = -1;
    int big = nodeArr.length;
    int index = 0;
    while (index != big) {
        if (nodeArr[index].value < pivot) {
            swap(nodeArr, index++, ++small);
        } else if (nodeArr[index].value > pivot) {
            swap(nodeArr, index, --big);
        } else {
            index++;
        }
    }
}

public static void swap(Node[] nodeArr, int a, int b) {
    Node tmp = nodeArr[a];
    nodeArr[a] = nodeArr[b];
    nodeArr[b] = tmp;
}

2)分成小、中、大三部分,再把各个部分之间串起来(面试用)

public static Node listPartition2(Node head, int pivot) {
    Node sH = null; // small head
    Node sT = null; // small tail
    Node eH = null; // equal head
    Node eT = null; // equal tail
    Node mH = null; // big head
    Node mT = null; // big tail
    Node next = null; // save next node
    Node node = head;
    while (node != null) {
        next = node.next;
        node.next = null;
        if (node.value < pivot) {
            if (sH == null) {
                sH = node;
                sT = node;
            } else {
                sT.next = node;
                sT = node;
            }
        } else if (node.value == pivot) {
            if (eH == null) {
                eH = node;
                eT = node;
            } else {
                eT.next = node;
                eT = node;
            }
        } else {
            if (mH == null) {
                mH = node;
                mT = node;
            } else {
                mT.next = node;
                mT = node;
            }
        }
        node = next;
    }
    if (sT != null) {
        sT.next = eH;
        eT = eT == null ? sT : eT;
    }
    if (eT != null) {
        eT.next = mH;
    }
    return sH != null ? sH : (eH != null ? eH : mH);
}


4.单链表的复制

—种特殊的单链表节点类描述如下

class Node {
    int value;
    Node next;
    Node rand;
    Node(int val) {
    	value = val; 
    }
}

rand指针是单链表节点结构中新增的指针, rand可能指向链表中的任意一个节点,也可能指向null。

给定一个由Node节点类型组成的无环单链表的头节点 head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。

【要求】
时间复杂度O(N)、额外空间复杂度O(1)

//使用map集合
public static Node copyRandomList1(Node head) {
    // key 老节点
    // value 新节点
    HashMap<Node, Node> map = new HashMap<Node, Node>();
    Node cur = head;
    while (cur != null) {
        map.put(cur, new Node(cur.val));
        cur = cur.next;
    }
    cur = head;
    while (cur != null) {
        // cur 老
        // map.get(cur) 新
        // 新.next ->  cur.next克隆节点找到
        map.get(cur).next = map.get(cur.next);
        map.get(cur).random = map.get(cur.random);
        cur = cur.next;
    }
    return map.get(head);
}

//使用有限空间
public static Node copyRandomList2(Node head){
    if (head==null){
        return head;
    }
    Node cur = head;
    Node next = null;
    while (cur!=null){
        next = cur.next;
        cur.next = new Node(cur.val);
        cur.next.next = next;
        cur = next;
    }
    cur = head;
    Node copy=null;
    while (cur!=null){
        next = cur.next.next;
        copy = cur.next;
        copy.random = cur.random==null?null:cur.random.next;
        cur = next;
    }
    Node res = head.next;
    cur = head;
    while (cur!=null){
        next = cur.next.next;
        copy = cur.next;
        cur.next = next;
        copy.next = next!=null?next.next:null;
    }
    return res;
}

5.两个单链表的交点

给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不相交,返回null

【要求】
如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)。

public static Node getIntersectNode(Node head1, Node head2) {
    if (head1 == null || head2 == null) {
        return null;
    }
    Node loop1 = getLoopNode(head1);
    Node loop2 = getLoopNode(head2);
    if (loop1 == null && loop2 == null) {
        return noLoop(head1, head2);
    }
    if (loop1 != null && loop2 != null) {
        return bothLoop(head1, loop1, head2, loop2);
    }
    return null;
}
// 找到链表第一个入环节点,如果无环,返回null
public static Node getLoopNode(Node head) {
    if (head == null || head.next == null || head.next.next == null) {
        return head;
    }
    Node fast = head.next;
    Node slow = head.next.next;
    while (fast.next != null && fast.next.next != null && fast != slow) {
        fast = fast.next.next;
        slow = slow.next;
    }
    if (fast.next == null || fast.next.next == null) {
        return null;
    }
    fast = head;
    while (fast != slow) {
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}
// 如果两个链表都无环,返回第一个相交节点,如果不想交,返回null
public static Node noLoop(Node head1, Node head2) {
    if (head1 == null || head1 == null) {
        return null;
    }
    int size = 0;
    Node cur1 = head1;
    Node cur2 = head2;
    while (cur1 != null) {
        cur1 = cur1.next;
        size++;
    }
    while (cur2 != null) {
        cur2 = cur2.next;
        size--;
    }
    if (cur1 != cur2) {
        return null;
    }
    cur1 = head1;
    cur2 = head2;
    while (size < 0) {
        cur2 = cur2.next;
    }
    while (size > 0) {
        cur1 = cur1.next;
        size--;
    }
    cur1 = head1;
    cur2 = head2;
    while (cur1 != null && cur2 != null && cur1 != cur2) {
        cur1 = cur1.next;
        cur2 = cur2.next;
    }
    return cur1;
}
// 两个有环链表,返回第一个相交节点,如果不想交返回null
public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
Node cur1 = null;
Node cur2 = null;
if (loop1 == loop2) {
    cur1 = head1;
    cur2 = head2;
    int n = 0;
    while (cur1 != loop1) {
        n++;
        cur1 = cur1.next;
    }
    while (cur2 != loop2) {
        n--;
        cur2 = cur2.next;
    }
    cur1 = n > 0 ? head1 : head2;
    cur2 = cur1 == head1 ? head2 : head1;
    n = Math.abs(n);
    while (n != 0) {
        n--;
        cur1 = cur1.next;
    }
    while (cur1 != cur2) {
        cur1 = cur1.next;
        cur2 = cur2.next;
    }
    return cur1;
} else {
    cur1 = loop1.next;
    while (cur1 != loop1) {
        if (cur1 == loop2) {
            return loop1;
        }
        cur1 = cur1.next;
    }
    return null;
}
}

6.能不能不给单链表的头节点,只给想要删除的节点,就能做到在链表上把这个点删掉?

不行,会有很多问题。

node = node.next;//将node.next的值传给node
node.next = node.next.next

这样无法删除最后一个节点,想要正确删除一个节点必须给单链表的head节点。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-12-02 17:01:12  更:2021-12-02 17:02:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 14:31:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码