第十三题 调整数组顺序使奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
方法一
遍历数组,借用两个数组分别存放奇偶,再将它们合并。时间复杂度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