两两交换节点位置:递归法、迭代法和数组转换法

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>两两交换节点位置</title>
</head>

<body>
    <script>
        class ANode {
            constructor(value) {
                this.value = value;
                this.next = null;
            }
        }

        // 从一个数组,创建一个nodeList并返回它的head
        function createNodeList(nodeList) {
            if(!nodeList || nodeList.length===0) return null;

            const head = new ANode(nodeList[0]);
            let cur = head;
            for(let i=0; i<nodeList.length-1; i++) {
                cur.next = new ANode(nodeList[i+1]);
                cur = cur.next;
            }
            return head;
        }

        // 打印nodeList, eg: nodeList: (1) => (2) => (3) => (4) => null
        function printNodeList(head, nodeListName = "nodeList") {
            let str = `(${head.value}) => `;
            while(head && head.next!==null) {
                head = head.next;
                str += `(${head.value}) => `;
            }
            str += "null";
            console.log(`${nodeListName}: ${str}`);
        }

        /**
         * @address: https://juejin.cn/post/7218584877910212667
         * @description: 递归回溯方法   TC:O(n)  SC:O(n)
         * @author: JunLiangWang
         * @param {*} node
         * @return {*}
         */
        function recursionBacktracking(node) {
            /**
             * 该方案利用递归回溯的方式,递归判断当前节点以及其后一个节点是否存在,
             * 如果不存在,证明已无节点可交换,则直接返回当前节点;否则,则将当前
             * 节点与其下一个节点进行交换。交换过程为:先将当前节点的下一个节点取
             * 出并保存(nextNode),然后将当前节点的下一个节点更新为nextNode后续
             * 节点继续递归的结果,然后将nextNode的下一个节点更新为当前节点,此时
             * nextNode则变更为当前节点的前一个节点,返回nextNode即可。
             */

            // 判断当前节点与其下一个节点是否存在,如果存在则将两节点位置交换
            if (node && node.next) {
                // 先保存当前节点的下一个节点(nextNode)
                let nextNode = node.next;
                // 将当前节点的下一个节点更新为nextNode后续节点继续递归的结果
                node.next = recursionBacktracking(nextNode.next);
                // 将nextNode的下一个节点更新为当前节点
                nextNode.next = node;
                // 此时nextNode已为当前节点的前一个节点,返回nextNode即可
                return nextNode;
            }
            // 不存在证明已无可交换节点,直接返回即可当前节点即可
            else {
                return node;
            }
        }

        /**
         * @address: https://juejin.cn/post/7218584877910212667
         * @description: 迭代法    TC:O(n)   SC:O(n)
         * @author: JunLiangWang
         * @param {*} head
         * @return {*}
         */
        function iteration(head) {
            /**
             * 该方案利用迭代,每次遍历链表的两个节点,将其交换,直至遍历结束
             */
            // 如果当前节点为空或其后一个节点为空,证明无需要交换的节点,直接返回当前节点即可
            if (!head || !head.next) return head;
            // 新的头节点为原链表的第二个节点
            const NEW_HEAD = head.next;
            // 上一个节点初值为空
            let lastNode = null;
            // 当前节点初值为原链表第一个节点
            let currentNode = head;
            // 当前节点不为空且其下一个节点也不为空,证明仍存在需要交换的节点
            while (currentNode && currentNode.next) {
                // 将当前节点的下一个节点取出保存(nextNode)
                let nextNode = currentNode.next;
                // 将当前节点的下一个节点更新为nextNode的下一个节点
                currentNode.next = nextNode.next;
                // nextNode的下一个节点更新为当前节点,此时位置已完成交换
                nextNode.next = currentNode;
                // 如果存在上一个节点,则需要将上一个节点的下一个节点更新为
                // 已经交换位置的nextNode
                if (lastNode) lastNode.next = nextNode;
                // 上一个节点更新为当前节点
                lastNode = currentNode;
                // 当前节点更新为其下一个节点
                currentNode = currentNode.next;
            }
            // 返回新链表
            return NEW_HEAD;
        }

        // 单向链表转换成数组
        function nodeListToArray(head) {
            if(!head) return [];

            const res = [head];
            while(head && head.next!==null) {
                head = head.next;
                res.push(head);
            }
            return res;
        }

        // 数组转换法
        function byArray(head) {
            const transformedArr = nodeListToArray(head);
            if(!transformedArr || transformedArr.length===0) return null;
            for(let i=0; i<transformedArr.length; i++) {
                let pre = transformedArr[i];
                let next = transformedArr[i+1];
                // 元素个数为奇数时,最后一个不交换
                if(next) {
                    [pre, next] = [next, pre];
                }
            }
            return createNodeList(transformedArr[0]);
        }

        const nodeList1 = createNodeList([1, 2, 3, 4]);
        const nodeList2 = createNodeList([1, 2, 3, 4]);
        const nodeList3 = createNodeList([1, 2, 3, 4]);
        printNodeList(nodeList1, "nodeList1");
        printNodeList(nodeList2, "nodeList2");
        printNodeList(nodeList3, "nodeList2");

        // 递归法
        const newHead1 = recursionBacktracking(nodeList1);
        printNodeList(newHead1, "递归法");

        // 迭代法
        const newHead2 = iteration(nodeList2);
        printNodeList(newHead2, "迭代法");

        // 数组转换法
        const newHead3 = byArray(nodeList3);
        printNodeList(newHead2, "数组转换法");
    </script>
</body>

</html>

image

posted @ 2023-04-06 15:59  pangqianjin  阅读(23)  评论(0编辑  收藏  举报