《剑指offer》第十三至十五题(js)

第十三题 调整数组顺序使奇数位于偶数前面

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

方法一

遍历数组,借用两个数组分别存放奇偶,再将它们合并。时间复杂度O(n),缺点可能就是要额外占用空间8…而且这样好像不太得算法精髓?

function reOrderArray(array)
{
    // write code here
    var odd = [], even = [];
    for(var i = 0; i < array.length; i++){
        if (array[i]%2 === 1){
            odd.push(array[i]);
        }else if (array[i]%2 === 0){
            even.push(array[i]);
        }
    }
    return odd.concat(even);
}

也可以用数组的filter方法做判断

运行时间:13ms,占用内存:5348k

方法二

不借用空间,即要在原数组上移动。因为奇和奇、偶和偶相对位置要不变,那么我们只要按顺序移动奇数或偶数就好。因为奇数要在前边,所以我们以第一个偶数为基准(第一个比最后一个好找),将奇数逐个移动至这个偶数的前一位,这样就完成啦。

function reOrderArray(array)
{
    var even01, temp;
    // 找出第一个偶数,标志为even01
    for (var i = 0; i < array.length; i++) {
        if (array[i]%2 === 0) {
            even01 = i;
            break;
        }
    }
    // 遍历数组
    for (var i = 0; i < array.length; i++) {
        // 如果为奇数
         if (array[i]%2 === 1) {
            //如果这个奇数在第一个偶数后面(因为如果在前面就不需要移动了)
             if(i > even01){
                // 把这个奇数存起来,它的位置要让出来给前面的偶数
                 temp = array[i];
                 // 将第一个偶数(包括它)和奇数之间的偶数都向后挪一个位置
                 for(var j = i; j > even01; j--){
                     array[j] = array[j-1];
                 }
                 // 把奇数放回第一个偶数让出来的位置,这时候奇数就在这个偶数前一位了
                 array[j] = temp;
                 // 这个偶数已经后移了,记得要修改标志位!!
                 even01 ++;
             }
         }  
    }
    return array;
}

运行时间:11ms,占用内存:5240k

第十四题 链表中倒数第K个节点

题目描述

输入一个链表,输出该链表中倒数第k个节点。

方法一

因为数组可以通过下标访问,借一个数组来存放这个链表。那么现在的问题就是找到倒数第K个节点的位置了。假设现在链表有三个节点,存到数组后,length = 3 :

  • 倒数第1个:下标为 2
  • 倒数第2个:下标为 1
  • 倒数第3个:下标为 0

发现没有!倒数第 n 个下标即为 len - n。

/*function ListNode(x){
    this.val = x;
    this.next = null;
}*/
function FindKthToTail(head, k)
{
    // write code here
    var arr = [];
    while (head){
        arr.push(head);
        head = head.next;
    }
    var len = arr.length;
    if (len < k)return false;
    else return arr[len-k];
}

运行时间:14ms,占用内存:5264k

方法二

在讨论区学习来的思路:设置两个指针 p1、p2,让 p2 先走 k-1 步,再让 p1、p2 一起走,直到 p2 走到末尾,这时候 p1 就指向倒数第 k 个节点了。也就是说,利用 p2 这个指针去控制这 K 个距离。妙(· 0 ·)ノ

/*function ListNode(x){
    this.val = x;
    this.next = null;
}*/
function FindKthToTail(head, k)
{
    // write code here
    if (head == null || k <= 0) return false;
    var p1 = p2 = head;
    // 让 p2 走
    for (var i = 0; i < k-1; i++) {
        if (p2.next != null){
            p2 = p2.next;
        }else return  false;  // 说明这个链表长度小于 k
    }
    // p1 p2 一起走,用 p2 控制
    while (p2.next){
        p1 = p1.next;
        p2 = p2.next;
    }
    return p1;
}


// 上面的代码优化版(牛):

function FindKthToTail(head, k)
{
    // write code here
    if (head == null || k <= 0) return false;
    var p1 = p2 = head;
    var i = 0;
    while (p2){
        if(i++ >= k)  p1 = p1.next;
        p2 = p2.next;
    }
    return i >= k ? p1 : null;
}

运行时间:13ms,占用内存:5348k

第十五题 反转链表

题目描述

输入一个链表,反转链表后,输出新链表的表头。

方法一

因为每个节点只存储了值和下一个节点,那么我们可以只改变值,不用改变节点。将链表的值存储起来,再反序输出。

/*function ListNode(x){
    this.val = x;
    this.next = null;
}*/
function ReverseList(pHead)
{
    // write code here
    if (pHead == null || pHead.next == null) return pHead; 
    var arr = [],node = pHead;
    // 用arr存储链表的值
    while (node){
        arr.push(node.val);
        node = node.next;
    }
    // 改变链表的值
    var newNode = pHead;
    for (var i = arr.length - 1; i >= 0; i--){
        newNode.val = arr[i];
        newNode = newNode.next;
    }
    return pHead;
}

运行时间:14ms,占用内存:5344k

在这里我犯了一个错误,没有将pHead的值存储起来,将 pHead = pHead.next 并返回了 pHead。然而这样 pHead 就会指向表尾的 next,也就是 null。所以,要注意指针的移动!

方法二

在代码区学习来的~ 上面的方法是改变值,这个方法是改变链表节点的 next 指向,也就是说,将节点的 next 指针指向上一节点。

第一个节点: next 设为 null ,因此 prev 初始值设为 null ,pHead.next = prev。但这样会把第一个节点切断,就取不到下一个节点了。因此,在给 next 赋值之前我们要先把下一个节点存起来,next=phead.next; 再用 next 来移动到下一个节点。到了下一个节点,同样是 pHead.next = prev,但这个prev应该是刚刚的那个节点,所以在移动前要把这个节点赋给 prev,prev=phead;

也就是说,用 prev 保存当前节点的值,移动到下一个节点,将下一个节点的 next 指向 prev。

function ReverseList(pHead)
{
    // write code here
    var phead=pHead;
    if(phead==null||phead.next==null) return phead;
    var prev=null;
    var next=null;
    while(phead!=null){
        next=phead.next; // 保存下一个节点
        phead.next=prev;  // 将 next 指为上一节点
        prev=phead; // 将 prev 保存为 当前节点
        phead=next; // 移动到下一节点
    }
    return prev;
}

运行时间:15ms,占用内存:5348k

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用南则详细说明了如何配置开发环境、部署项目以及常见问的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值