如何在双向链表中同时按数字和字母进行排序

huangapple go评论72阅读模式
英文:

how to sort both numerically and alphabetically in a doubly linked list

问题

我想创建一个双向链表,该链表可以与一个两步排序算法一起使用。首先,我们在列表中有一些电影。这些电影将按年份在列表中排序(每个新数据也将被排序),如果年份相同,那么这些年份将按字母顺序排序。

首先,我在互联网上做了一些研究,然后编写了两个方法。实际上,“else if ((headRef).movie.releaseDate == newNode.movie.releaseDate)” 方法的部分是我的。在编写这部分之前,我得到了以下结果。

1997 Titanic

1997 Fast & Furious

1999 The Matrix

2014 The Hobbit: The Battle of the Five Armies

2014 Interstellar

经过我的更正,我得到了以下结果。

1997 Titanic

1999 The Matrix

2014 The Hobbit: The Battle of the Five Armies

2014 Interstellar

有一部电影消失了,它们仍然没有按字母顺序排列。这对我来说很困难,我是新手。我可以做些什么来解决这个问题。

英文:

I want to make a doubly linked list that works with a two step sorting algorithm, first of all, we have some movies on the list. These movies will be sorted by year in the list (each new data will be sorted as well), if the years are the same, those years will be sorted alphabetically.

public static Node insertionSort(Node headRef) {
		
	    Node sorted = null;

	    Node current = headRef;
	    
	    while (current != null) {
	    	
	        Node next = current.next;

	        current.prev = current.next = null;
	 
	        sorted = sortedInsert(sorted, current);
	 
	        current = next;
	    	
	    }
	   
	    headRef = sorted;
	     
	    return headRef;
		
		
	}
public static Node sortedInsert(Node headRef, Node newNode) {
		
		 Node current;
		 
		 if (headRef == null) {
			 
			 headRef = newNode; 
		 } else if ((headRef).movie.releaseDate > newNode.movie.releaseDate) {
			 
			 newNode.next = headRef;
			 newNode.next.prev = newNode;
		     headRef = newNode;
		     
		} else if ((headRef).movie.releaseDate == newNode.movie.releaseDate) {

		
			int i = 0;
			boolean permission = true;
			
			do {
				

				char character = headRef.movie.movieName.charAt(i);
				int value = character;
				
				char character2 = newNode.movie.movieName.charAt(i);
				int value2 = character2;
				
				if(value > value2) {
					
					 newNode.next = headRef;
					 newNode.next.prev = newNode;
				     headRef = newNode;
				     
				     permission = false;
					
				} else if(value == value2) {
					i++;
					
				} 
				
				
			} while(permission);
		
		}
		
		 else {
			
			current = headRef;
		
		    while (current.next != null && current.next.movie.releaseDate < newNode.movie.releaseDate)
		            current = current.next;
		
		    newNode.next = current.next;

		    if (current.next != null)
		    	newNode.next.prev = newNode;
		 
		   current.next = newNode;
		   newNode.prev = current;
		   
		}
		    return headRef;

	}

First, I did some research on the internet, then I wrote two methods. actually the "else if ((headRef).movie.releaseDate == newNode.movie.releaseDate)" part of the method is mine. Before writing this section I was getting the following results.

1997 Titanic

1997 Fast & Furious

1999 The Matrix

2014 The Hobbit: The Battle of the Five Armies

2014 Interstellar

After my correction, I got the following results.

1997 Titanic

1999 The Matrix

2014 The Hobbit: The Battle of the Five Armies

2014 Interstellar

A movie disappeared and they are still not in alphabetical order. This is very difficult for me, I'm new.What can I do to solve this.

答案1

得分: 1

我认为问题出在最后的 else 部分,因为在检查 releaseDate 时,你没有应用字母顺序。

为了统一比较操作,最好让 Movie 类实现 Comparable<Movie> 接口,该接口定义了一个 compareTo 方法,这将让你在所有地方使用相同的条件进行比较。

Comparable 标记了该对象可以根据 compareTo 函数与其他对象进行比较,该函数返回:

  • 如果两个对象相等,则返回 0
  • 如果第一个对象小于第二个对象,则返回负数
  • 如果第一个对象大于第二个对象,则返回正数

并且在此函数中,你可以定义自己的逻辑来比较哪些字段。

class Movie implements Comparable<Movie>{

    ...

    @Override
    public int compareTo(Movie o) {
      if (releaseDate > o.releaseDate) {
        return 1;
      } else if (releaseDate < o.releaseDate) {
        return -1;
      }
      // 按字母顺序排序。
      return movieName.compareTo(o.movieName);
    }
}

为了简化代码,这是你代码的另一版本:

public static Node sortedInsert(Node headRef, Node newNode) {
    if (headRef == null) {
      // 如果没有头节点,则将新节点视为头节点
      headRef = newNode;
    } else if (headRef.movie.compareTo(newNode.movie) > 0) {
      // 在起始位置插入
      newNode.next = headRef;
      newNode.next.prev = newNode;
      headRef = newNode;
    } else {
      // 查找具有 movie > newNode.movie 的节点
      Node current = headRef;
      while(current.next != null && current.next.movie.compareTo(newNode.movie) < 0) {
        current = current.next;
      }

      newNode.next = current.next;
      current.next = newNode;
      newNode.prev = current;
    }

    return headRef;
  }
1997 Fast & Furious
1997 Titanic
1999 The Matrix
2014 Interstellar
2014 The Hobbit: The Battle of the Five Armies
英文:

I think the issue is at the final else because you don't apply the alphabetical order when you check the releaseDate.

To unify the comparison in one place it is better to let the Movie class to implement Comparable&lt;Movie&gt; which defines a compareTo method, this will let you use the same conditions everywhere

Comparable marks the object that it can be compared with other objects based on the compareTo function which returns:

  • 0 if the two objects are equal
  • negative number if the first object is smaller than the second one
  • positive number if the first object is larger than the second one

And in this function you can define your own logic of which fields you can compare

class Movie implements Comparable&lt;Movie&gt;{

    ...

    @Override
    public int compareTo(Movie o) {
      if (releaseDate &gt; o.releaseDate) {
        return 1;
      } else if (releaseDate &lt; o.releaseDate) {
        return -1;
      }
      // Alphabetical order.
      return movieName.compareTo(o.movieName);
    }
}

And to simplify the code a bit this is another version of your code:

public static Node sortedInsert(Node headRef, Node newNode) {
    if (headRef == null) {
      // No head then consider the new Node as the head
      headRef = newNode;
    } else if (headRef.movie.compareTo(newNode.movie) &gt; 0) {
      // Insert at the start position  
      newNode.next = headRef;
      newNode.next.prev = newNode;
      headRef = newNode;
    } else {
      // Search for the Node with movie &gt; newNode.movie
      Node current = headRef;
      while(current.next != null &amp;&amp; current.next.movie.compareTo(newNode.movie) &lt; 0) {
        current = current.next;
      }

      newNode.next = current.next;
      current.next = newNode;
      newNode.prev = current;
    }

    return headRef;
  }
1997 Fast &amp; Furious
1997 Titanic
1999 The Matrix
2014 Interstellar
2014 The Hobbit: The Battle of the Five Armies

答案2

得分: 0

以下是您的代码的一些评论:

  1. 您应该添加一个外部容器类来保存头/尾引用,然后将插入方法设置为非静态成员。
  2. 您正在进行自己的字符串比较,这是不必要的 - String类已经实现了Comparable(因此具有compareTo方法)。
  3. 您应该将列表类型设置为通用的,这样它可以与任何元素类型一起使用(即不仅仅是电影)。还建议通过接受Comparator参数来使列表的排序可配置。
  4. 双向链表因不得不处理各种边缘情况(空列表、一个元素列表、在头/尾插入值)而显得有些繁琐。如果添加虚拟节点来标记列表的头和尾,实现就会变得简单得多。

考虑到这些问题,可以如下实现列表:

public class NodeList<T> {

    private static class Node<T> {
        final T value;
        Node<T> next;
        Node<T> prev;

        private Node(T value, Node<T> next, Node<T> prev) {
            this.value = value;
            this.next = next;
            this.prev = prev;

            next.prev = this;
            prev.next = this;
        }

        private Node() {
            value = null;
        }
    }

    private final Comparator<T> comp;
    private final Node<T> beforeHead = new Node<>();
    private final Node<T> afterTail = new Node<>();

    public NodeList(Comparator<T> comp) {
        this.comp = comp;
        this.beforeHead.next = afterTail;
        this.afterTail.prev = beforeHead;
    }

    public void insert(T value) {
        for (Node<T> node = beforeHead; node.next != afterTail; node = node.next) {
            if (comp.compare(value, node.next.value) < 0) {
                new Node<T>(value, node.next, node);
                return;
            }
        }

        new Node<T>(value, afterTail, afterTail.prev);
    }
}

为了测试它,我们可以使它可迭代:

public class NodeList<T> implements Iterable<T> {
    
    // 之前的内容保持不变。

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            Node<T> node = beforeHead.next;

            @Override
            public boolean hasNext() {
                return node != afterTail;
            }

            @Override
            public T next() {
                final T value = node.value;
                node = node.next;
                return value;
            }
        };
    }
}

然后可以像这样测试它:

public static class Movie {
    public final int year;
    public final String name;

    public Movie(int year, String name) {
        this.year = year;
        this.name = name;
    }

    public int year() { return year;}
    public String name() { return name;}

    public String toString() {
        return year + " " + name;
    }
}

public static void main(String[] args) {
    final NodeList<Movie> movies = new NodeList<>(Comparator.comparing(Movie::year).thenComparing(Movie::name));
    movies.insert(new Movie(1997, "Titanic"));
    movies.insert(new Movie(1999, "The Matrix"));
    movies.insert(new Movie(2014, "The Hobbit: The Battle of the Five Armies"));
    movies.insert(new Movie(1997, "Fast & Furious"));
    movies.insert(new Movie(2014, "Interstellar"));

    movies.forEach(System.out::println);
}

这会输出以下内容:

1997 Fast & Furious
1997 Titanic
1999 The Matrix
2014 Interstellar
2014 The Hobbit: The Battle of the Five Armies

值得注意的是,插入排序对于除了小型列表之外的任何内容都相对低效。

英文:

Some comments regarding your code:

  1. You should add an outer container class to hold the head/tail references, and then make the insert method a non-static member.
  2. You're doing your own string comparison, which is unnecessary - the String class already implements Comparable (and therefore has a compareTo method).
  3. You should make your list type generic, so it can be used with any element type (i.e. not just Movies). It would also be advisable to make the ordering on the list configurable (by accepting a Comparator argument).
  4. Double-linked lists are a bit tedious due to to having to handle insertion for the various edge case (empty list, one element list, value inserted at the head/tail). The implemention becomes much more simple if you add dummy nodes to mark the head and the tail of the list.

Bearing all of that in mind, then the list can be implemented as follows:

public class NodeList&lt;T&gt; {
private static class Node&lt;T&gt; {
final T value;
Node&lt;T&gt; next;
Node&lt;T&gt; prev;
private Node(T value, Node&lt;T&gt; next, Node&lt;T&gt; prev) {
this.value = value;
this.next = next;
this.prev = prev;
next.prev = this;
prev.next = this;
}
private Node() {
value = null;
}
}
private final Comparator&lt;T&gt; comp;
private final Node&lt;T&gt; beforeHead = new Node&lt;&gt;();
private final Node&lt;T&gt; afterTail = new Node&lt;&gt;();
public NodeList(Comparator&lt;T&gt; comp) {
this.comp = comp;
this.beforeHead.next = afterTail;
this.afterTail.prev = beforeHead;
}
public void insert(T value) {
for (Node&lt;T&gt; node = beforeHead; node.next != afterTail; node = node.next) {
if (comp.compare(value, node.next.value) &lt; 0) {
new Node&lt;T&gt;(value, node.next, node);
return;
}
}
new Node&lt;T&gt;(value, afterTail, afterTail.prev);
}
}

To test it it helps if we make it Iterable:

public class NodeList&lt;T&gt; implements Iterable&lt;T&gt; {
// as before.
@Override
public Iterator&lt;T&gt; iterator() {
return new Iterator&lt;T&gt;() {
Node&lt;T&gt; node = beforeHead.next;
@Override
public boolean hasNext() {
return node != afterTail;
}
@Override
public T next() {
final T value = node.value;
node = node.next;
return value;
}
};
}
}

and then test it like this:

public static class Movie {
public final int year;
public final String name;
public Movie(int year, String name) {
this.year = year;
this.name = name;
}
public int year() { return year;}
public String name() { return name;}
public String toString() {
return year + &quot; &quot; + name;
}
}
public static void main(String[] args) {
final NodeList&lt;Movie&gt; movies = new NodeList&lt;&gt;(Comparator.comparing(Movie::year).thenComparing(Movie::name));
movies.insert(new Movie(1997, &quot;Titanic&quot;));
movies.insert(new Movie(1999, &quot;The Matrix&quot;));
movies.insert(new Movie(2014, &quot;The Hobbit: The Battle of the Five Armies&quot;));
movies.insert(new Movie(1997, &quot;Fast &amp; Furious&quot;));
movies.insert(new Movie(2014, &quot;Interstellar&quot;));
movies.forEach(System.out::println);
}

which outputs the following:

1997 Fast &amp; Furious
1997 Titanic
1999 The Matrix
2014 Interstellar
2014 The Hobbit: The Battle of the Five Armies

It's probably worth noting that insertion sorts are relatively inefficient for anything oher than small lists.

huangapple
  • 本文由 发表于 2023年5月28日 23:30:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76352233.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定