go merge ordered queues

Ordered queue merge


One of the interview questions is very good. I didn't answer them well when I did it. I'll summarize afterwards.

golang can merge any number of ordered arrays. The function prototype is as follows:

func Sort(ctx context.Context, out chan<- int, in ... <-chan int)

The ordered sequence is input by any number of chan, so it may increase at any time. The output is obtained by out chan


There are similar problems in leetcode, and the ideas are relatively simple. The difficulty of this problem is how to access the chan element, once it is taken out, it is not easy to put it back.

At that time, slicing was considered as buf, and the number traversed from chan was first put into slicing, and then traversed slicing to find the minimum number. The problem of slicing is that it is not easy to judge whether a number is taken out or put back. The next time you traverse chan, it's not easy to determine which slice has the returned value.

Then I will think of using chan as buf, using buf with length of 1. When it is full, it means there is still value, and when it is empty, it means supplement value.

So the idea is as follows: create a group of chan with length of 1 as buf, store the value from out chan to buf chan in each cycle, and then traverse buf chan to find the minimum value. After each comparison, put the larger value back to the original position of buf chan.

code implementation

The buf structure is defined as follows. Tmp is chan of length 1. The extracted elements are compared with Curr

type Head struct {
	Curr int
	Tmp chan int

There are still some details to deal with;

  • You need to use ctx.Done() to exit
  • When reading in value from in, you need to judge whether the reading is successful
  • Nonblocking reading is required when reading in values
  • If all the pipes are read, you can consider to exit after the count is 0, or the incoming ctx times out. Is there a better way?
  • Each round of comparison only reads one value from buf, and other elements need to be put back, so the position of the current minimum value should be recorded
  • Before traversing the Head access element, the length of Tmp must be determined

The code is as follows

func Sort(ctx context.Context, out chan<- int, in ... <-chan int) {
	//Initialize buf
	var heads [] Head
	for i:=0;i!=len(in);i++{
		heads = append(heads, Head{
			Tmp:make(chan int,1),
    ticker := time.NewTicker(time.Millisecond*10)
		select {
			case <-ctx.Done():
				// Processing exit
			case <-ticker.C:
				count := 0
				for i,head := range heads{
					//It's worth it. Don't read it
					if len(head.Tmp) == 1{
					//Nonblocking read in
					select {
					case v,ok :=<-in[i]:
						// Judge whether it is readable
						if ok{
							count ++
				if count == 0{
				// Mark the channel number and value of the minimum value
				minId :=-1
				var min int

				for i,head := range heads{
					// Skip empty
					if len(head.Tmp) == 0{
					// Initialization minimum
					if minId == -1{
						minId = i
						min = head.Curr
					// Compare minimum
					if minId !=-1{
						if head.Curr<min{
							//Put back old values
							minId = i
							min = head.Curr
						} else{
							//Put back the new value
				//Output min
				if minId!=-1{

The test example is as follows

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	out := make(chan int,100)
	in1 := make(chan int,10)
	in2 := make(chan int,10)
	for i:=0;i!=10;i++{

	for i:=0;i!=10;i++{

	go Sort(ctx, out, in1, in2)

	for i := range out{

Tags: Programming

Posted on Thu, 26 Mar 2020 10:35:40 -0400 by mikebyrne