Leetcode Go 1-2

题目

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

题解

使用Go语言实现

将两个链表合并成一个,并且注意按十进制进位即可,注意以下几点:

  1. 不要改变原链表,需要额外建立一个索引指向原链表头
  2. 两个原链表的长度都不定,遍历时,如果某一个结束,另一个还有,则需要将已经结束的按0值处理
  3. 进位时,最终的一个1不要忘记
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
	var lsum *ListNode = new(ListNode)
	var lz *ListNode = lsum
	var ls *ListNode = lsum
	var lx *ListNode = l1
	var ly *ListNode = l2
	var num1, num2, sum, inc int

	for { 
		if lx == nil && ly == nil {
			if inc != 0 {
				lz = new(ListNode)
				lz.Val = inc
				ls.Next = lz
				ls = lz
			}
			break;
		}

		if lx != nil {
			num1 = lx.Val
		} else {
			num1 = 0
		}
		
		if ly != nil {
			num2 = ly.Val
		} else {
			num2 = 0
		}

		lz = new(ListNode)
		ls.Next = lz
		ls = lz

		sum = num1 + num2 + inc
		if sum  < 10 {
			lz.Val = sum
			inc = 0
		} else {
			lz.Val = sum - 10
			inc = 1
		}

		if lx != nil {
			lx = lx.Next
		}

		if ly != nil {
			ly = ly.Next
		}	
	}

	return lsum.Next
}

一些可以用到的工具函数

//打印链表元素
func listPrint(List *ListNode) {
	for lx := List; lx != nil; lx = lx.Next {
		fmt.Printf("%d ", lx.Val)
	}
	fmt.Println()
}

//按照指定的数列创建链表
func listNew(nums ...int) *ListNode {
	var list *ListNode
	var lp *ListNode
	var lx *ListNode

	for i, x := range nums {
		if 0 == i {
			list = new(ListNode)
			list.Val = x
			list.Next = nil

			lp = list
		} else {
			lx = new(ListNode)
			lx.Val = x
			lx.Next = nil

			lp.Next = lx
			lp = lx
		}
	}

	return list
}

知识点

1、申请内存空间

Go使用new分配内存,返回指针类型,另外new创建的内存,不需要free/delete释放,Go会自动释放内存空间

type ListNode struct {
	Val int
	Next *ListNode
}

var lsum *ListNode = new(ListNode)

2、函数返回结构体指针

如下几种返回方式,都不会存在问题,Go对于堆和栈的处理,程序员无需关心

//函数中使用new来申请空间,然后将指针返回
func listCreate1() *ListNode {
	var list = new(ListNode)
	return list
}

//函数中创建空结构体,然后将指针返回
func listCreate2() *ListNode {
	var list = &ListNode {}
	return list
}

//函数中创建并初始化结构体,然后返回结构体指针
func listCreate3() *ListNode {
	var list = &ListNode {Val:100, Next:nil}
	return list
}

//函数中创建并初始化结构体的另一种方式,然后返回结构体指针
func listCreate4() *ListNode {
	var list = &ListNode {100, nil}
	return list
}

返回结果

四种返回方式返回的地址

0xc000006030
0xc000006038
0xc000006040
0xc000006048

注意这边函数中返回的是一个局部变量的地址,在C++中,返回局部变量的地址这种行为是非常可怕的,这个指针就会是一个野指针。而GO语言中支持这种语法,显然是有它的道理的。我这边参考了一篇博客:函数返回局部变量地址,他是这么说的:

go语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆。所以不用担心会不会导致memory leak,因为GO语言有强大的垃圾回收机制。go语言声称这样可以释放程序员关于内存的使用限制,更多的让程序员关注于程序功能逻辑本身。

所以函数内部局部变量,无论是动态new出来的变量还是创建的局部变量,它被分配在堆还是栈,是由编译器做逃逸分析之后做出的决定

所以,说到这里,来总结一下GO的一个特点:

GO程序员不需要关心定义的这个局部变量具体是在堆上,还是在栈上,即使是new出来的也不一定在堆上,GO编译器会对该变量做逃逸分析来确定变量的存放位置,即使是在堆上分配了,GO也有垃圾回收机制不用我们去主动释放空间,作为程序员我们只要考虑上层的这些逻辑就可以了,不需要关心底层具体的分配问题。

3、函数返回结构体本身

函数返回结构体时,是将结构体的值拷贝给接收者

    func listCreate5() ListNode {
        var list = ListNode {100, nil}
        fmt.Printf("list %p\n", &list)
        return list
    }

    var list5 = listCreate5()
    fmt.Printf("list5 %p\n", &list5)

返回结果

list 0xc0000323b0
list5 0xc0000323a0

4、可变参函数

//定义可变参函数,注意list...int表示一个参数列表和参数类型,内部实际上这是一个切片
func listPrint(name string, list...int) {
	for _, x := range list {
		fmt.Printf("%d ", x)
	}
	fmt.Printf("This is %s list\n", name)
}

//第一种调用方式
listPrint("chenlei", 1,2,3,4,5,6,7)

//第二种调用方式,注意直接传切片给可变参列表是不行的,但是可以使用...的方式
var nums = [] {1,2,3,4,5,6,7}
listPrint("chenlei", nums...)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值