leetcode刷题-从尾到头打印链表

题目来源:从尾到头打印链表

知识点

题解

我的解法

代码

首先不参考任何解析或答案,自己思考,我想到的解法是将链表转化为数组,再将数组倒序输出即可

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reversePrint(head *ListNode) []int {
	transArr := transList2Arr(head)
	listLen := len(transArr)
	if listLen == 0 {
		return []int{}
	}
	var retArr []int
	for i := listLen - 1; i >= 0; i-- {
		retArr = append(retArr, transArr[i])
	}
	return retArr
}

func transList2Arr(head *ListNode) []int {
	var ret []int
	if head == nil {
		return ret
	}
	ret = append(ret, head.Val)
	if head.Next != nil {
		ret = append(ret, transList2Arr(head.Next)...)
	}
	return ret
}

分析

执行用时:52 ms
在所有 Go 提交中击败了 6.42% 的用户
内存消耗:9.6 MB, 在所有 Go 提交中击败了 5.34% 的用户

这很难评,可以说是太糟糕了。。。
其实这样写效率是很低的,首先是一个递归,时间复杂度是O(n),空间复杂度是O(n),然后是对数组进行倒序操作,时间复杂度为O(n),空间复杂度为O(1),这个版本我可以确定离最优解差得很远,去分析下别人的代码看下解法

参考其他答案后的解法

代码

我参考的是评论区的一版java代码,参考代码如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {

    public int[] reversePrint(ListNode head) {
        int size = 0;
        ListNode node = head;
        while(node != null){
            size++;
            node = node.next;
        }
        int[] intVals = new int[size];
        node = head;
        while(node != null){
            intVals[--size] = node.val;
            node = node.next;
        }
        return intVals;
    }
}

作者:多米诺克
链接:https://leetcode.cn/leetbook/read/illustration-of-algorithm/5dt66m/?discussion=3NHbEj
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

根据这个代码我写出一版新的go代码

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reversePrint(head *ListNode) []int {
	transArr := transList2Arr(head)
	listLen := len(transArr)
	if listLen == 0 {
		return []int{}
	}
	var retArr []int
	for i := listLen - 1; i >= 0; i-- {
		retArr = append(retArr, transArr[i])
	}
	return retArr
}

func transList2Arr(head *ListNode) []int {
	var retArr []int
	for {
		if head != nil {
			retArr = append(retArr, head.Val)
			head = head.Next
		} else {
			break
		}
	}
	return retArr
}

执行结果:通过
执行用时:0 ms, 在所有 Go 提交中击败了 100.00% 的用户
内存消耗:3.3 MB, 在所有 Go 提交中击败了52.28%的用户

主要的改动是transList2Arr方法,不再使用递归的方式来转化链表,直接通过循环来实现。这里原本其实还可以优化,可以在循环中计数,这样就不用再计算转化后的数组长度了,但经我实践,go计算数组长度的方法len(v type)要比直接计数执行更快占用内存更小,因此不做这个“优化”。

题解

提供了两种解题思路:
(1)递归法
(2)辅助栈法
递归法和我原本的递归差距不大,辅助栈法需要用到栈结构,这里使用container/list包实现:

func reversePrint(head *ListNode) []int {
	transArr := transList2Arr(head)
	return transArr
}

func transList2Arr(head *ListNode) []int {
	stack := list.New() //初始化栈
	for {
		if head != nil {
			stack.PushFront(head.Val) //入栈
			head = head.Next
		} else {
			break
		}
	}
	var retData []int
	stackLen := stack.Len()
	for i := 0; i < stackLen; i++ {
		data := stack.Remove(stack.Front()).(int) //出栈
		retData = append(retData, data)
	}
	return retData
}

效率比循环要低一些,严格来说时间复杂度和空间复杂度都是一样的,看了下原评论区,大概有这些解释

首先一个栈帧复杂度比单纯查询一次并写一个值要更复杂,其次头插效率是比尾插要低的,另外变长数组如果不首先进行分配内存,在插入过程中会存在多次分配的问题,所以总体上来说先查询长度,分配内存,然后倒序写入是效率最高的
作者:YYYCZ
链接:https://leetcode.cn/circle/discuss/1zLeBA/view/G4eZJq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解析里给的解法单论时空效率肯定是不如直接这么做的。
第一个方法中系统站占的内存肯定比你直接压入显式的变量栈用的内存多;第二个方法须知vector变长数组的增加长度的方式是在现有长度上额外增加一倍,也就是说并非只是和显式栈共用了n*sizeof(int)的内存,而是更多(当然数量级上依然是O(n)的)。
这种简单题单靠测试是的随机误差就足以在测试样例里拉开差距了,常数上的效率差别是不容忽视的。
作者:鹤留宫
链接:https://leetcode.cn/circle/discuss/1zLeBA/view/adFIsN/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

反正都看不懂就是了。。。就酱。。。

你可能感兴趣的:(leetcode,链表,算法)