常见算法题

36 阅读4分钟

1 题号:902 小于n的最大数

1.1 题目

  • 给一个按非递减排列的整数数组,给定一个n值,用这个数组中的值来组合不大于n的最大值,数组中元素可以重复使用

1.2 思路:

  • 先把数组从大到小排序,然后从中选取数字来组成数字,
  • 这里有个问题:如果n中有数小于nums中的最小值,那么就不应该再在同位置上num是 > n中对应位的时候跳过了

1.3 go实现:

// 小于n的最大数
func maxValueLessThanN(nums []int, n int) int {
	sort.Slice(nums, func(i, j int) bool { return nums[i] > nums[j] })
	res := -1
	digits := getDigits(n)

	var backtrack func(curIdx, curNum int, digitSameToNum bool)
	backtrack = func(curIdx, curNum int, digitSameToNum bool) {
		if curIdx == len(digits) {
			if curNum <= n {
				res = max(curNum, res)
			}
			return
		}
		digit := digits[curIdx]
		for _, num := range nums {
			if digitSameToNum && num > digit {
				continue
			}
			curNum = curNum*10 + num
			backtrack(curIdx+1, curNum, digit == num)
		}
	}
	backtrack(0, 0, true)
	return res
}

func getDigits(n int) []int {
	digits := []int{}

	for n > 0 {
		digits = append([]int{n % 10}, digits...) // 高位放在切片的前面
		n = n / 10
	}
	return digits
}

func TmaxValueLessThanN() {
	nums := []int{4, 2, 5, 7}
	n := 67921
	fmt.Println(maxValueLessThanN(nums, n))
}

1.4 总结:

  • 注意当digit和num不等的时候就可以在nums中任意选了,因为是nums是从大到小排列的,所以任意选就是选最大的

2 题号:415 大数相加

2.1 题目

  • 给定两个数字字符串,将两个数相加

2.2 思路:

  • 用i和j指向两个字符串的最后一个字符,用carry表示是否有进位
  • 只要i和j中有一个 >=0或者有进位,就可以继续遍历

2.3 go实现:

func addStrings(num1 string, num2 string) string {
    carray := 0
    res := ""

    for i,j := len(num1)-1, len(num2)-1;i >= 0 || j>= 0|| carray != 0;i,j = i - 1, j-1{
        var n1 , n2 int
        
        if i >= 0{
            n1 = int(num1[i] - '0')
        }
        if j >= 0{
            n2 = int(num2[j] - '0')
        }

        intRes := n1 + n2 + carray
        carray = intRes / 10
        res = strconv.Itoa(intRes % 10) + res
    }

    return res
}

2.4 总结:

  • go中字符串中一个字符减去一个字符的值不是int类型,需要转成int

3 题号:192 字符串转数字

3.1 题目

  • 给定一个字符串转成数字,字符串中只有数字、大小写字母、正负号和下划线,如果超出int32的最大数就返回int32的最大数

3.2 思路:

  • 先把前导空格去掉
  • 用一个变量保存符号
  • 从前到后遍历排除其他字符保留数字

3.3 go实现:

func myAtoi(str string) int {
	str = strings.TrimSpace(str)
	res := 0
	sign := 1

	for i, v := range str {
		if i == 0 && v == '+' {
			sign = 1
		} else if i == 0 && v == '-' {
			sign = -1
		} else if int(v - '0') >= 0 && int(v -'0')<= 9{
			res = res*10 + int(v-'0')
		}else{
			break
		}

		if res > math.MaxInt32 {
			if sign == 1 {
				return math.MaxInt32
			} else {
				return math.MinInt32
			}
		}
	}
	return res * sign
}

3.4 总结:

  • 可以使用strings包来去掉前导空格

题号:链表排序

题目

  • 给定一个无序链表,将这个链表排序后返回头结点

思路:

  • 归并排序,链表从前到后,先把一个节点当做一个链表,两两排序,然后两个节点当做一个链表排序

go实现:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */

func merge(head1, head2 *ListNode)*ListNode{
    dummyHead := &ListNode{}
    cur := dummyHead

    for head1 != nil && head2 != nil{
        if head1.Val < head2.Val{
            cur.Next = head1
            head1 = head1.Next
        }else{
            cur.Next = head2
            head2 = head2.Next
        }
        cur = cur.Next
    }

    if head1 != nil{
        cur.Next = head1
    }
    if head2 != nil{
        cur.Next = head2
    }
    return dummyHead.Next
}

func sortList(head *ListNode) *ListNode {
    if head == nil{
        return head
    }    

    listLen := 0
    for cur := head; cur != nil; cur = cur.Next{
        listLen++
    }
    dummyHead := &ListNode{Next: head}
    for curLen := 1; curLen < listLen; curLen = curLen*2{
        prev, cur := dummyHead, dummyHead.Next
        for cur != nil{
            // 确定链表1
            head1 := cur
            for i := 1; i < curLen && cur.Next != nil; i++{
                cur = cur.Next
            }
            // 确定链表2
            head2 := cur.Next
            cur.Next = nil
            cur = head2
            for i := 1; i < curLen && cur != nil && cur.Next != nil;i++{
                cur = cur.Next
            }
            // 确定下一轮的开始
            var next *ListNode
            if cur != nil{
                next = cur.Next
                cur.Next = nil
            }
            prev.Next = merge(head1, head2)
            for prev.Next != nil{// 移动前一节点
                prev = prev.Next
            }
            cur = next
        }
    }

    return dummyHead.Next
}

总结:

  • 对每一个节点要清晰:拆分链表、记录每一轮的prev,next

题号:146 lru

题目

  • 就是lru

思路:

  • hash表 + 链表

go实现:

type Node struct{
    key int
    val int
    pre *Node
    next *Node
}

type LRUCache struct {
    head *Node
    tail *Node
    size int
    cap int
    hash map[int]*Node
}


func Constructor(capacity int) LRUCache {
    hash := make(map[int]*Node)
    head := &Node{}
    tail := &Node{}

    lru := LRUCache{
        head:head,
        tail:tail,
        hash:hash,
        cap:capacity,
        size:0,
    }

    lru.head.next = lru.tail
    lru.tail.pre = lru.head
    return lru
}

func (this *LRUCache)addAtHead(node *Node){
    node.pre = this.head
    node.next = this.head.next
    this.head.next.pre = node
    this.head.next = node
}

func (this *LRUCache)moveToHead(node *Node){
    node.next.pre = node.pre
    node.pre.next = node.next
    this.addAtHead(node)
}

func (this *LRUCache)removeTail(){
    delete(this.hash, this.tail.pre.key)
    
    this.tail.pre.pre.next = this.tail
    this.tail.pre = this.tail.pre.pre
    this.size--
}

func (this *LRUCache) Get(key int) int {
    if v, exists := this.hash[key]; !exists{
        return -1
    }else{
        this.moveToHead(v)
        return v.val
    }
}


func (this *LRUCache) Put(key int, value int)  {
    if v, exists := this.hash[key];!exists{
        if this.size == this.cap{
            this.removeTail()
        }
            newNode := &Node{key:key, val:value}
            this.hash[key] = newNode
            this.addAtHead(newNode)
            this.size++
        
    }else{
        v.val = value
        this.moveToHead(v)
    }
}


/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */

总结: