clllll  

单链表结构


public class Node<V> {
    V value;
    Node next;
}

双链表

public class DoubleNode<V> {
    V value;
    DoubleNode next;
    DoubleNode last;
}

反转单向链表

public static Node reverseList(Node curNode) {
    Node pre = null;
    Node next = null;
    while (curNode != null) {
        next = curNode.next; // 获取下一个node
        curNode.next = pre; //将当前node的下一个指向前一个
        pre = curNode; //更新 前一个Node
        curNode = next; // 更新 当前Node 
    }
    return pre;
}

反转双向链表

public static DoubleNode reverseNode(DoubleNode head) {
    DoubleNode curNode = head;
    DoubleNode pre = null;
    DoubleNode next = null;
    while (curNode.next != null) {
        next = curNode.next;
        curNode.next = pre;
        curNode.pre = next;
        pre = curNode;
        curNode = next;

    }
    return pre;
}

打印俩个 有序链表 的公共部分

俩个指针,谁小谁移动,相等打印,然后都移动。 一个到头停止

public static void printCommonPart(Node node1, Node node2) {
    while (node1 != null && node2 != null) {
        if (node1.value < node2.value) {
            // 谁小谁移动
            node1 = node1.next;
        } else if (node1.value > node2.value) {
            node2 = node2.next;
        } else {
            // 一样打印
            System.out.print(node1.value + " ");
            // 都移动
            node1 = node1.next;
            node2 = node2.next;
        }
    }
}

判断一个链表是否为回文结构

对称轴是虚的, 或者不是虚的

  • 使用栈,全部 先入栈,然后比较
  • 只放右边的数据。省一半空间,快慢指针,快指针走俩步,慢指针走一步,快指针走到结尾,慢指针就到中间的部分 了
    考虑奇数 偶数

public static boolean isPalindromeList_01(Node head) {
    // 判断一个链表是否为回文结构。使用栈,简单,空间复杂度较高
    Stack<Node> stack = new Stack<>();
    // 入栈
    Node curNode = head;
    while (curNode != null) {
        stack.push(curNode);
        curNode = curNode.next;
    }
    // 出栈比较
    while (head != null) {
        Node stackNode = stack.pop();
        if (stackNode.value != head.value) {
            return false;
        }
        head = head.next;
    }

    return true;
}

只添加右半部分,使用快慢指针

public static boolean isPalindromeList_02(Node head) {
    // 判断一个链表是否为回文结构。使用栈,简单,空间复杂度较高
    Stack<Node> stack = new Stack<>();

    Node f = head; // 快指针
    Node s = head; // 慢指针

    while (f != null) {
        f = f.next;
        if (f != null) {
            f = f.next;
        } else {
            break;
        }
        s = s.next;

    }
    System.out.println("===s-> " + s.value);
    while (s != null) {
        stack.push(s);
        s = s.next;
    }

    while (!stack.empty()) {
        Node stackNode = stack.pop();
        if (head.value != stackNode.value) {
            return false;
        }
        head = head.next;
    }
    return true;
}

快慢指针判断优化:
注意:判断是否为空,使用快指针。

while (f.next != null && f.next.next != null) {
            s = s.next;
            f = f.next.next;
}
  • 如果要求 空间复杂度为O(1)
    通过修改链表,也是快慢指针,但是不用栈了,当慢指针到了中间位置,后续的node 逆序,然后 向中间走,依次比较。
public static boolean isPalindromeList_03(Node head) {
        if (head == null || head.next == null) {
            return true;
        }

        Node s = head;
        Node f = head;
        while (f.next != null && f.next.next != null) {
            s = s.next;
            f = f.next.next;
        }
        // 右边第一个node
        Node right_first_node = s.next;
        // Node right_node = right_first_node;
        // 将中间的下一个 next置为null
        s.next = null;

        // 将右边的逆序
        Node right_pre = null;
        Node right_next = null;
        while (right_first_node != null) {
            right_next = right_first_node.next; // 获取下一个node
            right_first_node.next = right_pre; // 当前node 的下一个指向前一个node
            right_pre = right_first_node; // 更新 pre
            right_first_node = right_next; // 更新 当前node 为下一个
        }

        // 保存右边 最后一个node,就是right_pre,
        Node right_last_node = right_pre;
        // 左边第一个 node
        Node left_first_node = head;

        boolean res = true;

        // 循环结束条件,下一个元素为空
        while (left_first_node != null && right_last_node != null) {
            if (left_first_node.value != right_last_node.value) {
                // 如果出现不等,就退出
                res = false;
                break;
            }
            // 更新左右 node 为下一个
            left_first_node = left_first_node.next;
            right_last_node = right_last_node.next;
        }

        // 恢复 链表,
        // 中间node 指向右边第一个,
        right_first_node = s.next;

        // 继续逆序
        // 将右边的逆序

        Node right_recover_pre = null;
        Node right_recover_next = null;
        while (right_last_node != null) {
            right_recover_next = right_last_node.next;
            right_last_node.next = right_recover_pre;
            right_recover_pre = right_last_node;
            right_last_node = right_recover_next;

        }
        return res;
    }

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

要求:相对顺序不变。时间复杂度O(N) , 空间复杂度O(1)

借助6个变量
SH: 小于某值的 头 ST: 小于某值的 尾
EH: 等于某值的 头 ET: 等于某值的 尾
BH: 大于某值的 头 BT: 大于某值的 尾

某个区域可能没有,注意空指针。

public static Node smallEqualBigger(Node head, int pivot) {

    Node sh = null;
    Node st = null;

    Node eh = null;
    Node et = null;

    Node bh = null;
    Node bt = null;

    Node next = null;

    while (head != null) {
        next = head.next;
        head.next = null; // 把当前node 搞成独立的,不然都连起来了。
        if (head.value < pivot) {
            // 当前node 小于 pivot
            if (sh == null) {
                // 如果 当前 sh 为null
                sh = head;
                st = head;
            } else {
                st.next = head; // 指向下一个
                st = head;

            }

        } else if (head.value == pivot) {
            // 当前 node 等于 pivot
            if (eh == null) {
                eh = head;
                et = head;
            } else {
                et.next = head;
                et = head;
            }

        } else {
            // 当前 node 大于 pivot
            if (bh == null) {
                bh = head;
                bt = head;
            } else {
                bt.next = head;
                bt = head;
            }
        }

        head = next; // 更新 head

    }
    // 拼接 st -> eh et -> bh
    // 考虑三个区域都可能为null
    if (st != null) {
        // 如果小于区域 不为 null
        st.next = eh;
        // 如果等于区域 为null, et就可以 置为 st;
        et = et == null ? st : et;

    }

    if (et != null) {
        // 如果等于区域不为null
        et.next = bh;
    }
    // 如果sh 不为null 就返回sh
    // 如果sh 为空就返回 eh
    // 在继续判断eh
    // 如果eh不为空,就返回 eh
    // 如果eh 为空, 就返回 bh
    return sh != null ? sh : eh != null ? eh : bh;

}

复制还有随机指针节点的链表

Node 还有一个属性就是 多了一个node,可能指向任何 链表内的节点,包括自己。

  • 用额外空间。使用hashmap key=老node value = 克隆的node.然后依次遍历找到关系
    RandomNode
public static class RandomNode {
        public int value;
        public RandomNode next;
        public RandomNode rand;

        public RandomNode(int value) {
            this.value = value;
        }
    }
public static RandomNode copyListWithRandom_01(RandomNode head) {
    HashMap<RandomNode, RandomNode> map = new HashMap<>();

    RandomNode cur = head;

    // 构造hashmap , key = node , value = 复制的node
    while (cur != null) {
        map.put(cur, new RandomNode(cur.value));
        cur = cur.next;
    }

    cur = head;
    while (cur != null) {
        // 给复制的node 链接 next 和 rand
        map.get(cur).next = map.get(cur.next);
        map.get(cur).rand = map.get(cur.rand);
        cur = cur.next;
    }
    return map.get(head);

}
  • 不使用额外空间 构造如下图,一对一对遍历。 random就是 next.next
public static RandomNode copyListWithRandom_02(RandomNode head) {
    RandomNode cur = head;
    RandomNode next = null;
    RandomNode cur_copy = null;
    // 构造为 1-> 1' -> 2 -> 2'
    while (cur != null) {
        cur_copy = new RandomNode(cur.value); // copy当前node
        next = cur.next; // 将当前node 的next 保存
        cur.next = cur_copy; // 将当前node 的next 指向 copynode
        cur_copy.next = next; // copynode的 next指向 当前节点的next
        cur = next; // 更新 cur
    }

    // 开始给 copynode 链接 rand node;
    cur = head;
    RandomNode curRand = null;
    while (cur != null) {
        curRand = cur.rand;
        // 要判断 curRand 是否为null
        cur.next.rand = curRand != null ? curRand.next : null;
        // 更新 cur
        cur = cur.next.next;

    }

    // split ,去掉原始元素
    RandomNode res = head.next;
    cur = head;

    while (cur != null) {
        next = cur.next.next; // cur.next 一定不是空的哈。不需要担心null
        // 把复制的节点的下一个copynode 指向下一个node的 copynode,
        cur.next.next = next != null ? next.next : null;// 要判断 next 为空哈。
        // 更新cur
        cur = next;
    }

    return res;
}
posted on 2022-05-02 13:48  llcl  阅读(53)  评论(0编辑  收藏  举报