数据结构与算法之堆: LeetCode 23. 合并K个升序链表 (Ts, Py, Go版)

关于112G和56G PAM4信号的有效回损(ERL) 本文探讨了回波损耗(RL)对高速数字通信系统性能的影响,重点分析了时域信道运行裕度(COM)与频域回波损耗指标的关联性。研究表明,传统频域回波损耗指标在预测系统性能时存在假阳性/假阴性问题,特别是对PAM-4信号系统影响更为显著。通过引入基于时域脉冲响应的有效回波损耗(ERL)指标,并结合判决反馈均衡器(DFE)的补偿效应,可更准确评估系统性能。实验表明,短封装器件虽然频域回波损耗较差,但在实际系统中表现更优。研究提出通过时域门控加权等方法优化ERL计算,为高速互连设计提供了更可靠的性能评估框架。 阅读详情

合并K个升序链表

  • https://leetcode.cn/problems/merge-k-sorted-lists/

描述

  • 给你一个链表数组,每个链表都已经按升序排列
  • 请你将所有链表合并到一个升序链表中,返回合并后的链表

示例 1

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2

输入:lists = []
输出:[]

示例 3

输入:lists = [[]]
输出:[]

提示

  • k == lists.length
  • 0 <= k <= 10410^4104
  • 0 <= lists[i].length <= 500
  • -10410^4104 <= lists[i][j] <= 10410^4104
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10410^4104

Typescript 版算法实现


1 )方案1:使用堆
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

class MinHeap {
    heap: Array<ListNode | null> = [];
    // 交换节点位置
    swap(i, j) {
    	if (i === j) return;
        [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
    }
    // 获得父节点
    getParentIndex(i) {
        return (i - 1) >> 1;
    }
    // 获取左子节点
    getLeftIndex(i) {
        return (i << 1) + 1; // 极客写法
    }
    // 获取右子节点
    getRightIndex(i) {
        return (i << 1) + 2;
    }
    // 上移操作封装 是个递归
    shiftUp(i) {
        // 如果到了堆顶元素,index是0,则不要再上移了
        if(!i) return;
        // 获得父节点下标: pi
        const pi = this.getParentIndex(i);
        // 开始做比较
        if(this.heap[pi]?.val > this.heap[i].val) {
            // 实现交换
            this.swap(pi, i);
            // 继续尝试上移操作
            this.shiftUp(pi);
        }
    }
    // 下移操作
    shiftDown(i) {
        // 如果到了堆尾元素,则不要再下移了
        if(i >= this.heap.length - 1) return;
        const li = this.getLeftIndex(i); // 左孩子索引
        const ri = this.getRightIndex(i); // 右孩子索引
        // 左孩子节点的值 < 当前节点的值 注意:左右对比中,this.heap[i]不能用变量替代,因为内部有swap和shiftDown,使用变量会导致bug
        if(this.heap[li]?.val < this.heap[i].val) {
                this.swap(li, i);
                this.shiftDown(li);
        }
        // 同样,对右孩子节点的值 < 当前节点的值
        if(this.heap[ri]?.val < this.heap[i].val) {
                this.swap(ri, i);
                this.shiftDown(ri);
        }
    }
    // 插入
    insert(value) {
        this.heap.push(value);
        this.shiftUp(this.heap.length - 1);
    }
    // 删除堆顶
    pop() {
        if(this.size() === 1) return this.heap.shift();
        const top = this.heap[0];
        // 变相删除堆顶, 堆尾元素移动到堆顶
        this.heap[0] = this.heap.pop(); // 删除数组的最后一个元素并返回,返回值赋值给堆顶元素
        // 执行堆顶下移操作,维持堆的有序性
        this.shiftDown(0);
        return top;
    }
    // 获取堆顶
    peak() {
        return this.heap[0];
    }
    // 获取堆的大小
    size() {
        return this.heap.length;
    }
}

function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
    const res = new ListNode(0);
    let p = res;
    const h = new MinHeap();
    // 通过输入来构建堆结构
    // 三个链表,堆中存入的是三个链表的头
    lists.forEach(l => {
        l && h.insert(l); // l是个链表,其实用的是链表头表示,也就是链表的第一个元素
    })
    // while循环,每一轮pk的都是堆内的元素
    // 谁出队,就把谁的下一个入堆,逐个比较
    // 最终堆空了,比较全部结束
    while(h.size()) {
        const n = h.pop(); // 弹出堆顶元素,最小值
        p.next = n; // 将堆顶元素挂载在新链表上
        p = p.next; // 链表指针移位,为下次连接做准备
        n.next && h.insert(n.next) // 将弹出的堆顶元素的下一个元素入堆,进行重新构建堆结构
    }
    return res.next; // 返回的是next, 因为其第一个节点是我们new出来,用于连接的,所以不包含第一个节点
}
  • 时间复杂度 O(nlogk)
    • forEach O(k), k是链表数量
    • while循环遍历了所有链表的所有节点 O(n),n是所有链表节点之和
    • 并且堆操作是O(logk), 两者结合:O(nlogk)
    • 整体:O(nlogk)
  • 空间复杂度 O(k)
    • 就是堆的大小
  • 堆能高效、快速找出最大值和最小值,时间复杂度O(1),堆顶是最大值或最小值
  • 找出第K个最大(小)元素
    • 构建一个容量为k的堆,让每个元素都插入这个堆
    • 保持容量始终为k, 最终堆顶就是最大元素或最小元素
  • 这个解法不算精妙,但是是两个数据结构(堆和链表)融合解题的典型

2 )方案2: 使用优先队列(堆)合并,优化方案1

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

class Status {
    val: number;
    ptr: ListNode;

    constructor(val: number, ptr: ListNode) {
        this.val = val;
        this.ptr = ptr;
    }
}

class PQ<T> {
    private heap: T[];

    constructor() {
        this.heap = [];
    }

    get length(): number {
        return this.heap.length;
    }

    private compare(a: T, b: T): number {
        if (a instanceof Status && b instanceof Status) {
            return a.val - b.val;
        }
        throw new Error('Invalid comparison types');
    }

    push(item: T): void {
        this.heap.push(item);
        this._siftUp(this.heap.length - 1);
    }

    pop(): T | undefined {
        if (this.heap.length === 0) return undefined;
        const lastElement = this.heap.pop();
        if (this.heap.length === 0) return lastElement;
        const returnItem = this.heap[0];
        this.heap[0] = lastElement!;
        this._siftDown(0);
        return returnItem;
    }

    private _siftUp(index: number): void {
        while (index > 0) {
            const parentIndex = Math.floor((index - 1) / 2);
            if (this.compare(this.heap[parentIndex], this.heap[index]) > 0) {
                [this.heap[parentIndex], this.heap[index]] = [this.heap[index], this.heap[parentIndex]];
                index = parentIndex;
            } else {
                break;
            }
        }
    }

    private _siftDown(index: number): void {
        const size = this.heap.length;
        while (true) {
            let smallest = index;
            const leftChild = 2 * index + 1;
            const rightChild = 2 * index + 2;

            if (leftChild < size && this.compare(this.heap[leftChild], this.heap[smallest]) < 0) {
                smallest = leftChild;
            }

            if (rightChild < size && this.compare(this.heap[rightChild], this.heap[smallest]) < 0) {
                smallest = rightChild;
            }

            if (smallest !== index) {
                [this.heap[index], this.heap[smallest]] = [this.heap[smallest], this.heap[index]];
                index = smallest;
            } else {
                break;
            }
        }
    }
}

function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
    const queue = new PQ<Status>();

    for (const node of lists) {
        if (node !== null) {
            queue.push(new Status(node.val, node));
        }
    }

    const head = new ListNode(0);
    let tail = head;

    while (queue.length > 0) {
        const f = queue.pop();
        if (f) {
            tail.next = f.ptr;
            tail = tail.next;
            if (f.ptr.next !== null) {
                queue.push(new Status(f.ptr.next.val, f.ptr.next));
            }
        }
    }

    return head.next;
}

3 )方案3: 顺序合并

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
    let ans: ListNode | null = null;
    for (let i = 0; i < lists.length; ++i) {
        ans = mergeTwoLists(ans, lists[i]);
    }
    return ans;
}

function mergeTwoLists(a: ListNode | null, b: ListNode | null): ListNode | null {
    if (a === null || b === null) {
        return a !== null ? a : b;
    }
    const head = new ListNode(0);
    let tail = head;
    let aPtr = a;
    let bPtr = b;

    while (aPtr !== null && bPtr !== null) {
        if (aPtr.val < bPtr.val) {
            tail.next = aPtr;
            aPtr = aPtr.next;
        } else {
            tail.next = bPtr;
            bPtr = bPtr.next;
        }
        tail = tail.next;
    }
    tail.next = aPtr !== null ? aPtr : bPtr;
    return head.next;
}

4 )方案4: 分治合并

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
    return merge(lists, 0, lists.length - 1);
}

function merge(lists: Array<ListNode | null>, l: number, r: number): ListNode | null {
    if (l == r) {
        return lists[l];
    }
    if (l > r) {
        return null;
    }
    const mid = (l + r) >> 1;
    return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
}

function  mergeTwoLists(a: ListNode | null, b: ListNode | null): ListNode | null {
    if (a === null || b === null) {
        return a !== null ? a : b;
    }
    const head = new ListNode(0);
    let tail = head;
    let aPtr = a;
    let bPtr = b;

    while (aPtr !== null && bPtr !== null) {
        if (aPtr.val < bPtr.val) {
            tail.next = aPtr;
            aPtr = aPtr.next;
        } else {
            tail.next = bPtr;
            bPtr = bPtr.next;
        }
        tail = tail.next;
    }
    tail.next = aPtr !== null ? aPtr : bPtr;
    return head.next;
}

Python3 版算法实现


1 ) 方案1:使用堆

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class MinHeap:
    def __init__(self):
        self.heap = []

    def swap(self, i, j):
        self.heap[i], self.heap[j] = self.heap[j], self.heap[i]

    def get_parent_index(self, i):
        return (i - 1) // 2

    def get_left_index(self, i):
        return (i << 1) + 1

    def get_right_index(self, i):
        return (i << 1) + 2

    def shift_up(self, i):
        if i == 0:
            return
        pi = self.get_parent_index(i)
        if self.heap[pi].val > self.heap[i].val:
            self.swap(pi, i)
            self.shift_up(pi)

    def shift_down(self, i):
        if i >= len(self.heap) - 1:
            return
        li = self.get_left_index(i)
        ri = self.get_right_index(i)
        if li < len(self.heap) and self.heap[li].val < self.heap[i].val:
            self.swap(li, i)
            self.shift_down(li)
        if ri < len(self.heap) and self.heap[ri].val < self.heap[i].val:
            self.swap(ri, i)
            self.shift_down(ri)

    def insert(self, value):
        self.heap.append(value)
        self.shift_up(len(self.heap) - 1)

    def pop(self):
        if len(self.heap) == 1:
            return self.heap.pop()
        top = self.heap[0]
        self.heap[0] = self.heap.pop()
        self.shift_down(0)
        return top

    def peak(self):
        return self.heap[0]

    def size(self):
        return len(self.heap)

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        res = ListNode(0)
        p = res
        h = MinHeap()
        for l in lists:
            if l:
                h.insert(l)
        while h.size() > 0:
            n = h.pop()
            p.next = n
            p = p.next
            if n.next:
                h.insert(n.next)
        return res.next

2 )方案2:使用优先队列(堆)合并,优化方案1

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Status:
    def __init__(self, val, ptr):
        self.val = val
        self.ptr = ptr

    def __lt__(self, other):
        return self.val < other.val

class PriorityQueue:
    def __init__(self):
        self.heap = []

    def push(self, item):
        self.heap.append(item)
        self._sift_up(len(self.heap) - 1)

    def pop(self):
        if not self.heap:
            raise IndexError("pop from an empty priority queue")
        last_element = self.heap.pop()
        if self.heap:
            return_item = self.heap[0]
            self.heap[0] = last_element
            self._sift_down(0)
            return return_item
        return last_element

    def _sift_up(self, index):
        while index > 0:
            parent_index = (index - 1) // 2
            if self.heap[parent_index] > self.heap[index]:
                self.heap[parent_index], self.heap[index] = self.heap[index], self.heap[parent_index]
                index = parent_index
            else:
                break

    def _sift_down(self, index):
        size = len(self.heap)
        while True:
            smallest = index
            left_child = 2 * index + 1
            right_child = 2 * index + 2

            if left_child < size and self.heap[left_child] < self.heap[smallest]:
                smallest = left_child

            if right_child < size and self.heap[right_child] < self.heap[smallest]:
                smallest = right_child

            if smallest != index:
                self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
                index = smallest
            else:
                break

    def is_empty(self):
        return len(self.heap) == 0

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        queue = PriorityQueue()
        for node in lists:
            if node:
                queue.push(Status(node.val, node))
        
        head = ListNode(0)
        tail = head
        
        while not queue.is_empty():
            f = queue.pop()
            tail.next = f.ptr
            tail = tail.next
            if f.ptr.next:
                queue.push(Status(f.ptr.next.val, f.ptr.next))
        
        return head.next

3 )方案3:顺序合并

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        ans = None
        for l in lists:
            ans = self.mergeTwoLists(ans, l)
        return ans

    def mergeTwoLists(self, a, b):
        if a is None or b is None:
            return a if a is not None else b
        head = ListNode(0)
        tail = head
        aPtr, bPtr = a, b
        while aPtr is not None and bPtr is not None:
            if aPtr.val < bPtr.val:
                tail.next = aPtr
                aPtr = aPtr.next
            else:
                tail.next = bPtr
                bPtr = bPtr.next
            tail = tail.next
        tail.next = aPtr if aPtr is not None else bPtr
        return head.next

4 )方案4:分治合并

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        return self.merge(lists, 0, len(lists) - 1)

    def merge(self, lists, l, r):
        if l == r:
            return lists[l]
        if l > r:
            return None
        mid = (l + r) // 2
        return self.mergeTwoLists(self.merge(lists, l, mid), self.merge(lists, mid + 1, r))

    def mergeTwoLists(self, a, b):
        if a is None or b is None:
            return a if a is not None else b
        head = ListNode(0)
        tail = head
        aPtr, bPtr = a, b
        while aPtr is not None and bPtr is not None:
            if aPtr.val < bPtr.val:
                tail.next = aPtr
                aPtr = aPtr.next
            else:
                tail.next = bPtr
                bPtr = bPtr.next
            tail = tail.next
        tail.next = aPtr if aPtr is not None else bPtr
        return head.next

Golang 版算法实现


1 ) 方案1:使用堆

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


// MinHeap 定义最小堆
type MinHeap struct {
	heap []*ListNode
}

// swap 交换堆中两个元素的位置
func (h *MinHeap) swap(i, j int) {
	h.heap[i], h.heap[j] = h.heap[j], h.heap[i]
}

// getParentIndex 获取父节点的索引
func (h *MinHeap) getParentIndex(i int) int {
	return (i - 1) / 2
}

// getLeftIndex 获取左子节点的索引
func (h *MinHeap) getLeftIndex(i int) int {
	return 2*i + 1
}

// getRightIndex 获取右子节点的索引
func (h *MinHeap) getRightIndex(i int) int {
	return 2*i + 2
}

// shiftUp 向上调整堆
func (h *MinHeap) shiftUp(i int) {
	if i == 0 {
		return
	}
	pi := h.getParentIndex(i)
	if h.heap[pi].Val > h.heap[i].Val {
		h.swap(pi, i)
		h.shiftUp(pi)
	}
}

// shiftDown 向下调整堆
func (h *MinHeap) shiftDown(i int) {
	size := len(h.heap)
	if i >= size-1 {
		return
	}
	li := h.getLeftIndex(i)
	ri := h.getRightIndex(i)
	minIndex := i
	if li < size && h.heap[li].Val < h.heap[minIndex].Val {
		minIndex = li
	}
	if ri < size && h.heap[ri].Val < h.heap[minIndex].Val {
		minIndex = ri
	}
	if minIndex != i {
		h.swap(minIndex, i)
		h.shiftDown(minIndex)
	}
}

// Insert 插入新元素到堆中
func (h *MinHeap) Insert(value *ListNode) {
	h.heap = append(h.heap, value)
	h.shiftUp(len(h.heap) - 1)
}

// Pop 弹出堆顶元素
func (h *MinHeap) Pop() *ListNode {
	if len(h.heap) == 1 {
		popped := h.heap[0]
		h.heap = h.heap[:0]
		return popped
	}
	top := h.heap[0]
	h.heap[0] = h.heap[len(h.heap)-1]
	h.heap = h.heap[:len(h.heap)-1]
	h.shiftDown(0)
	return top
}

// Peek 获取堆顶元素但不弹出
func (h *MinHeap) Peek() *ListNode {
	if len(h.heap) == 0 {
		return nil
	}
	return h.heap[0]
}

// Size 获取堆的大小
func (h *MinHeap) Size() int {
	return len(h.heap)
}

// mergeKLists 合并 K 个升序链表
func mergeKLists(lists []*ListNode) *ListNode {
	res := &ListNode{Val: 0}
	p := res
	h := &MinHeap{}

	// 将所有链表的头节点插入堆中
	for _, l := range lists {
		if l != nil {
			h.Insert(l)
		}
	}

	// 从堆中弹出最小元素,并将其下一个节点(如果存在)插入堆中
	for h.Size() > 0 {
		n := h.Pop()
		p.Next = n
		p = p.Next
		if n.Next != nil {
			h.Insert(n.Next)
		}
	}
	return res.Next
}

2 )方案2:使用优先队列(堆)合并,优化方案1

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

// Status represents the status of a node in the priority queue.
type Status struct {
	val int
	ptr *ListNode
}

// PriorityQueue is a min-heap of Status.
type PriorityQueue []*Status

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool { return pq[i].val < pq[j].val }

func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }

func (pq *PriorityQueue) Push(x interface{}) {
	*pq = append(*pq, x.(*Status))
}

func (pq *PriorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	*pq = old[0 : n-1]
	return item
}

func (pq *PriorityQueue) Fix(i int) {
	n := pq.Len()
	for {
		j := i
		// Check left child
		if l := 2*i + 1; l < n && pq.Less(l, j) {
			j = l
		}
		// Check right child
		if r := 2*i + 2; r < n && pq.Less(r, j) {
			j = r
		}
		if j == i {
			break
		}
		pq.Swap(i, j)
		i = j
	}
}

func (pq *PriorityQueue) Up(i int) {
	for {
		parent := (i - 1) / 2
		if parent == i || !pq.Less(i, parent) {
			break
		}
		pq.Swap(i, parent)
		i = parent
	}
}

func (pq *PriorityQueue) PushFix(x interface{}) {
	*pq = append(*pq, x.(*Status))
	n := pq.Len()
	pq.Up(n - 1)
}

func (pq *PriorityQueue) PopFix() interface{} {
	old := *pq
	n := len(old)
	item := old[0]
	old[0] = old[n-1]
	*pq = old[0 : n-1]
	pq.Fix(0)
	return item
}

func mergeKLists(lists []*ListNode) *ListNode {
	queue := &PriorityQueue{}
	for _, node := range lists {
		if node != nil {
			queue.PushFix(&Status{val: node.Val, ptr: node})
		}
	}
	head := &ListNode{Val: 0}
	tail := head
	for queue.Len() > 0 {
		f := queue.PopFix().(*Status)
		tail.Next = f.ptr
		tail = tail.Next
		if f.ptr.Next != nil {
			queue.PushFix(&Status{val: f.ptr.Next.Val, ptr: f.ptr.Next})
		}
	}
	return head.Next
}

3 ) 方案3:顺序合并

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

func mergeKLists(lists []*ListNode) *ListNode {
	ans := (*ListNode)(nil)
	for _, l := range lists {
		ans = mergeTwoLists(ans, l)
	}
	return ans
}

func mergeTwoLists(a, b *ListNode) *ListNode {
	if a == nil || b == nil {
		if a != nil {
			return a
		}
		return b
	}
	head := &ListNode{Val: 0}
	tail := head
	aPtr, bPtr := a, b
	for aPtr != nil && bPtr != nil {
		if aPtr.Val < bPtr.Val {
			tail.Next = aPtr
			aPtr = aPtr.Next
		} else {
			tail.Next = bPtr
			bPtr = bPtr.Next
		}
		tail = tail.Next
	}
	if aPtr != nil {
		tail.Next = aPtr
	} else {
		tail.Next = bPtr
	}
	return head.Next
}

4 ) 方案4:分治合并

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

func mergeKLists(lists []*ListNode) *ListNode {
	return merge(lists, 0, len(lists)-1)
}

func merge(lists []*ListNode, l, r int) *ListNode {
	if l == r {
		return lists[l]
	}
	if l > r {
		return nil
	}
	mid := (l + r) >> 1
	return mergeTwoLists(merge(lists, l, mid), merge(lists, mid+1, r))
}

func mergeTwoLists(a, b *ListNode) *ListNode {
	if a == nil || b == nil {
		if a != nil {
			return a
		}
		return b
	}
	head := &ListNode{Val: 0}
	tail := head
	aPtr, bPtr := a, b
	for aPtr != nil && bPtr != nil {
		if aPtr.Val < bPtr.Val {
			tail.Next = aPtr
			aPtr = aPtr.Next
		} else {
			tail.Next = bPtr
			bPtr = bPtr.Next
		}
		tail = tail.Next
	}
	if aPtr != nil {
		tail.Next = aPtr
	} else {
		tail.Next = bPtr
	}
	return head.Next
}
基于YOLOv10的建筑图纸物体检测系统:从数据集构建到UI界面实现 高度结构化的图形元素标准化的符号和标注系统多层次的细节表达精确的尺寸和位置关系。 阅读详情

相关推荐

LangFuse部署与实践

LangFuse的原理主要是基于可观察性和分析技术来监控和优化低延迟消息(LLM)应用。它提供了一套完整的解决方案,通过收集、处理和分析LLM应用在运行过程中产生的各种数据,来生成实时的、深入的、可操作的见解。通过提供实时的、深入的见解和可视化的跟踪功能,LangFuse使得识别和解决应用性能问题变得更加简单和高效。总的来说,LangFuse的原理是通过综合运用可观察性和分析技术,来提供对LLM应用的全面监控和优化能力。这使得开发者和运维团队能够更加高效地管理和维护他们的应用,从而提升用户体验和业务效益。

pumpkin84514的博客 7948

数据结构与算法笔记:字典序最大问题分析

字典序最大问题 问题描述 给定一个1到n的排列(无序状态的),依次插入到栈中,在每时每刻都可以多次从栈中弹出栈顶 问:应如何使得弹出栈顶的序列的字典序最大,并输出这个序列 问题分析 什么是字典序 比如两个序列:5,4,3,2,1和1,2,3,4,5 这两个中第一个的字典序最大 每个位置上的数字比其他序列上同一位置的数字要大 比如:第一位置中5比1大,第二位置中4比2大这样 即:字典序最大 = 从左向右看这个序列,每个数字都尽量大 题目给定一个1到n的排列,每个数字只出现一次 要使出栈的字典序最大

Wang的专栏 4393

从零搭建AGV避障系统:我用激光雷达+树莓派视觉模块踩过的那些坑

本文分享了基于树莓派与国产激光雷达从零搭建低成本AGV避障系统的实战经验。针对复杂仓库环境,详细剖析了激光雷达数据噪声、视觉模块算力瓶颈、IMU漂移等核心挑战,并提供了多传感器融合、软件架构设计与避障决策的具体解决方案,为中小型自动化改造项目提供了宝贵参考。

git9versioner的博客 985

数据结构与算法笔记:农夫过河问题python3实现

问题描述 农夫(Human)过河。一个农夫带着一只狼(Wolf),一只羊(Sheep)和一些菜(Vegetable)过河。河边只有一条船,由于船太小,只能装下农夫和他的一样东西。在无人看管的情况下,狼要吃羊,羊要吃菜,请问农夫如何才能使三样东西平安过河? 要求: 输出一行,为最终的过河方式,方案格式为 过河人员、回来人员、过河人员、回来人员、…、过河人员。 过去和回来的人员之间,用空格隔开。 以四...

Wang的专栏 3374

水下井口装置及钻杆建模

内容:海上油气资源开采的过程中,通常会由海上钻井平台下入钻杆连接到海底井口装置。OrcaFlex软件可进行各种海洋工程结构动态分析,通过各项数据的输入,能够很方便地进行水下井口装置及钻杆建模。直接使用软件New Line及new vessel功能,再设置模型的各项参数(几何属性、弯曲刚度、轴线刚度、扭转刚度、泊松比等)即可建成井口装置及钻杆模型。 设置管线的几何属性:主要包括管线的内外径、壁厚、...

s0302017的博客 1148

树莓派5使用CSI摄像头 | 树莓派5接上CSI摄像头后命令行操作显示检测无设备

此系列用于记录树莓派5的一些操作,这篇文章讲述树莓派5如何使用CSI摄像头。

beyourself_he的博客 8796

数据结构与算法笔记:计算思维之经典农夫过河问题C++实现

农夫、羊、狼、菜的过河问题 问题描述 角色:农夫,羊,狼,菜 条件1:船很小,只能装下农夫和其他一个角色 条件2:无人看管,羊吃菜,狼吃羊 问:如何让其他三种角色被农夫平安带着过河? 相关分析 我们可以先用人脑尝试一下相关渡河策略 1 ) 先渡狼,人回来再渡菜,人回来再渡羊 2 ) 先渡菜,人回来再渡狼,人回来再渡羊 以上两种很好想到,而且我们可以知道羊这种角色是不能先渡河过去的,羊需要最后再渡 如果先渡了羊了,之后再渡任何其他角色,都要将羊带回来 可以参考小游戏:http://gameschool

Wang的专栏 3312

OpenCV和Halcon分别实现彩色图像的白平衡效果

实现白平衡算法中的灰度世界法,能有效改善图像发红/发蓝/发绿的现象 1、OpenCV #include <opencv2/opencv.hpp> using namespace cv; int main() { Mat g_srcImage,dstImage; vector<Mat> g_vChannels; g_srcImage = im...

$firecat利白的代码足迹$ 2830

数据结构与算法笔记:图搜索之DFS详解

图搜索 Graph Search 图搜索的分类 BFS广度优先(宽搜) DFS深度优先(深搜) !!!本文详解!!! 深度优先搜索DFS 深度优先遍历DFS, 这个策略其实是非常stupid or simple的,比BSF要简单的多 同样,我们可以通过一个故事来开始 在古希腊神话中, Ariadne是Crete的国王Minos的公主,她给忒修斯(Theseus)一个线团 使得忒修斯成功进入迷宫杀死牛头怪兽,但事后忒修斯抛弃了她 这是一个有道德意义的故事又或者存在不同的版本, 但不是我们的研究的对象

Wang的专栏 3119

(五)C++自制植物大战僵尸游戏LoadingScene的实现讲解

游戏启动后就会立即切换到游戏加载场景中。只有游戏资源文件加载完成后,才能进入游戏。Loadingscene类继承Cocos2d-x中的Scene父类,表明Loadingscene是一个场景类。切换到LoadingScene场景中,首先会创建该场景,会调用函数,在该函数中调用自己定义的方法实现指定功能。代码文件的位置如下图所示。

尔灵尔亿的博客 2007

QT应用编程: 编写低功耗BLE蓝牙调试助手(Android系统APP)

一、环境介绍 QT版本: 5.12.6 编译环境: win10 64位 目标系统: Android 完整工程源码下载地址(包含APK文件):https://download.csdn.net/download/xiaolong1126626497/19051696 二、功能介绍 本软件是一款BLE蓝牙串口调试助手,支持常规的发送和接收调试,BLE是低功耗类型蓝牙,在智能家居、物联网领域使用较多。BLE低功耗蓝牙一般不能直接使用手机进行连接,手机蓝牙默认都是工作在传统蓝牙模式,无法直接连接BL..

7663

数据结构与算法笔记:计算思维之人鬼渡河问题

人鬼渡河问题 1 ) 问题描述 目标:将东岸的3人3鬼通过一只小船安全转移到西岸,希望摆渡次数尽可能少 条件 船的容量有限,一次最多只能坐2人(或2鬼或1人1鬼) 无论是在河的东岸还是在河的西岸,一旦鬼数多于人数,则人将被鬼吃掉 怎样渡河的大权掌握在人的手中 说明:划船的时间忽略不计。船一靠岸即将船与岸视为一体,人和鬼即使还没有下 船也视为已上岸 任务:编写程序,求出一种渡河方案 备注:图片托管于github,请确保网络的可访问性 2 ) 分析 目标是找

Wang的专栏 2447

​​手把手教你学PCIE--ATU 的 Bar 匹配模式与地址匹配模式详解​

摘要: ATU(地址转换单元)提供两种地址匹配模式:Bar匹配模式基于PCI设备的BAR寄存器自动映射虚拟地址(只需配置物理基址),适用于标准PCI设备DMA;地址匹配模式需显式配置虚拟与物理地址范围,适合自定义映射场景。核心区别在于匹配依据(BAR自动推导vs人工定义),Bar模式简化配置,地址模式更灵活。

MHD0815的博客 380

数据结构与算法之二叉树: LeetCode 701. 二叉搜索树中的插入操作 (Ts版)

1 ) 方案1:模拟。2 ) 方案2:递归。

Wang的专栏 2270

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

数据结构与算法笔记:计算思维之下楼梯台阶和象棋跳马问题

下台阶问题 1 ) 问题描述 从楼上走到楼下共有 h 个台阶,每一步有三种走法 走一个台阶; 走二个台阶; 走三个台阶。 问:一共可以走出多少种方案? 即共要多少步? 每一步走几级台阶? 2 ) 分析 初始思路 我们根据题目给定的场景,先简单模拟一下,设定 h=4h=4h=4 备注:图片托管于github,请确保网络的可访问性 如上图所示, 这里有4阶楼梯, 红色数字代表每一步走几级台阶 这里枚举了4层台阶的所有可能情况, 但是不管具体有几级台阶,下

Wang的专栏 2111

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Python笔记:滑动验证码的识别算法

概述 用程序识别极验滑动验证码的验证,包括分析识别思路、识别缺口位置、生成滑块拖动路径、模拟实现滑块拼合通过验证等步骤。 准备工作 使用Python库是Selenium,浏览器为Chrome。请确保已安装Selenium库和ChromeDriver浏览器驱动。 关于极验滑动验证码: 极验滑动验证码官网为:http://www.geetest.com/ 验证方式为拖动滑块拼合图像,若图像完全拼合...

Wang的专栏 2103

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

数据结构与算法之堆: LeetCode 295. 数据流的中位数 (Ts版)

/ 返回 1.5 ((1 + 2) / 2)中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。3 ) 方案3:有序集合 + 双指针。2 ) 方案2:优先队列-优化版。1 ) 方案1:优先队列。

Wang的专栏 1907

数据结构与算法笔记:计算思维之谁做的好事与刑侦作案问题

谁做的好事 清华附中有四位同学中的一位做了好事,不留名,表扬信来了之后,校长问这四位是谁做的好事。 A说:不是我。 B说:是C。 C说:是D。 D说:他胡说。 已知:三个人说的是真话,一个人说的是假话。 现在请你根据这些信息,编写程序找出做了好事的人。 分析 将四个人说的四句话(自然语言)转换成计算机可以计算的式子, 可使用循环枚举的方式来判断 (1) 思路一:通过字符表示人 先定义一个字符变量 thisman 表示要寻找的做了好事的人的“名字(代号)”(A、B、C、D),即 char this

Wang的专栏 1830

数据结构与算法笔记:图搜索之BFS详解

图搜索 Graph Search 图搜索的分类 BFS广度优先(宽搜) DFS深度优先(深搜) 广度优先搜索BFS 我们可以用比喻来说明广度优先搜索算法 在一片草木枯黄的深秋草原上,在草原的某一处出现了一处野火 一开始的时候野火集中于一点之上,在这点野火耗尽当前植被变成灰烬之前点燃了周围的植被 比如节点s是初始火种,假设我们手中有一个秒表,每过1秒,我们的大火会向外迈进一步 这个过程只能向外,不能向内,因为只能点燃植被,不能把灰烬点燃 蓝色的点是即将变为灰烬的点,红色的点是刚被点燃的点,灰色的圆形或

Wang的专栏 1793

数据结构与算法之动态规划: LeetCode 1494. 并行课程 II (Ts版)

给你一个整数 n 表示某所大学里课程的数目,编号为 1 到 n ,数组 relations 中, relations[i] = [xi, yi] 表示一个先修课的关系,也就是课程 xi 必须在课程 yi 之前上。解释:上图展示了题目输入的图。一个最优方案是:第一学期上课程 2 和 3,第二学期上课程 4 ,第三学期上课程 1 ,第四学期上课程 5。然后第二个学期上课程 1 ,第三个学期上课程 4。在一个学期中,你 最多 可以同时上 k 门课,前提是这些课的先修课在之前的学期里已经上过了。

Wang的专栏 1749

数据结构与算法笔记:求直方图最大面积算法分析

直方图最大面积 问题描述 有n列的直方图,第j列高度为hjh_jhj​, 求一个面积最大的子矩阵 备注:图片托管于github,请确保网络的可访问性 格式要求 输入第一行正整数n,第二行n个空格隔开的非负整数 数据范围: 所有直方图的列不会超过30000 关键算法实现 1 ) 算法1:时间复杂度为: O(n3)O(n^3)O(n3)的实现 分析 矩形面积为底和高的乘积 底部从a到b,那么底边长为: b-a+1 高为:min{hi∣a≤i≤b}min \{h

Wang的专栏 1664

数据结构与算法之LRU: 实现 LRU 缓存算法功能 (Ts, Py, Go版)

LRU - Lease Recently Used 最近使用如果内存优先,只缓存最近使用的,删除 ‘沉睡’ 数据核心 api: get set分析。

Wang的专栏 1599

数据结构与算法之贪心: LeetCode 455. 分饼干 (Ts, Py, Go版)

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j]。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。2 )排序 + 双指针 + 贪心。1 )排序 + 遍历。

Wang的专栏 1568

动态规划(Dynamic Programming)详解:从入门到 LeetCode Hot 100

将一个复杂问题拆分成多个规模更小的子问题,通过保存子问题的计算结果,避免重复计算,最终得到原问题的答案。先看一个经典问题。dp[i] = 以第 i 个元素结尾的最优结果例如最长递增子序列。最长递增子序列不一定以最后一个元素结尾,因此必须遍历整个数组取最大值。dp[i] = 前 i 个元素的最优结果这种情况下,最终答案通常就是dp[n]。关键在于理解 dp 数组的定义。动态规划的核心并不是背诵某个固定模板,而是建立正确的状态定义,并通过状态转移方程解决问题。dp[i] 或 dp[i][j] 表示什么?

zengjiajun2006的博客 885

Hash表:两数之和、字母异位词分组、最长连续序列 [力扣 Hot100]

三道高频哈希表应用题:两数之和(利用HashMap快速查找补数)、字母异位词分组(排序后作为键分组)、最长连续序列(通过HashSet去重并仅从序列起点扩展)。核心思想均为借助哈希结构优化查找效率,避免重复计算。同时提及双指针等其他解法思路,体现算法设计中“空间换时间”的典型策略。

2403_87560480的博客 226

力扣73 矩阵置零

该解法通过标记含0的行和列来实现矩阵置零。遍历矩阵,用布尔数组记录有0的行和列;再遍历一次,若当前行或列被标记,则置为0。时间复杂度O(mn),空间复杂度O(m+n)。

Pniubi的博客 34
上一篇: 数据结构与算法之堆: LeetCode 347. 前K个高频元素 (Ts, Py, Go版)
下一篇: 数据结构与算法之堆: 实现最大堆类的两种方式及实现堆排序 (Ts, Py, Go版)
Wang's Blog
博客等级 码龄12年 7438粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值