繁体   English   中英

旋转双向链表

[英]Rotate Doubly Linked List

我正在尝试顺时针和逆时针旋转双向链表。 但是我的代码只输出列表的一部分。 我知道如果有 0 或 1 个元素,列表不应旋转。 如果值大于 1,我想向右旋转。另外,如果值小于 0,我想向左旋转。我不确定我的列表做错了什么。 逻辑非常混乱。 由于 addLast 方法有效,因此故意不包括在内。

public class Node
    {
        public Node next;
        public Node previous;
        public Object data;

    }
public class LinkedList
    {
        private Node head;
        private Node tail;
        public Node rotate(int k)
        {
            if (head == null )
            {
                return head;
            }
            Node node = head;

            k = k % listSize(head);
           
            if (k == 0 || k == 1)
            { 
                return head;
            }
             //Rotates
            while (node != null && k > 1)
            {
                node = node.next;
                k--;
            }

            Node newhead = node.next;
            newhead.previous = null;
            node.next = null;
            Node temp = newhead;
            while (temp.next != null)
            {
                temp = temp.next;
            }

            newhead = head;
            node.previous = temp.previous;
            tail = node;

            return newhead;
        }

        public int listSize(Node node)
        {
            if(node == null)
            {
                return 0;
            }
            int count = 0;
            while(node != null)
            {
                count++;
                node = node.next;
            }
            return count;

        }

        public void addLast(Object data)
        {
            Node toAdd = new Node();
            toAdd.data = data;
            toAdd.next = null;
            if (head == null)
            {
                head = toAdd;
                tail = null;
            }
            else
            {
                if (tail == null)
                {
                    head.next = toAdd;
                    tail = toAdd;
                }
                else
                {
                    tail.next = toAdd;
                    tail = toAdd;
                }
            }
        }
}
 static void Main(string[] args)
        {
            Console.WriteLine("Add Last: ");
            LinkedList myList2 = new LinkedList();

            myList2.addLast("Tim");
            myList2.addLast("Nick");
            myList2.addLast("Julie");
            myList2.addLast("Jessie");
            myList2.addLast("Jordan");
            myList2.addLast("Peter");
            myList2.addLast("John");
             myList2.rotate(2);
}

编辑:在阅读任何评论之前,我尝试过重写我的代码。 它仍然可以成功轮换列表。

是的,这很艰难,我也遇到过很多同样的问题。 有帮助的一件事是重命名一些变量,以便让我清楚地知道发生了什么。

在旋转 function 中:

        public Node rotate(int k)
        {
            if (head == null)
            {
                return head;
            }

            k = -k;  
            k = k % listSize(head);
            if (k<0) { k+= listSize(head); }

            if (k == 0 )
            {
                return head;
            }
            //Rotates
            Node newhead = head;

            while (newhead != null && k > 0)
            {
                newhead = newhead.next;
                k--;
            }
            Node newtail = newhead.previous;
            
            newhead.previous = null;
            newtail.next = null;

            tail.next = head;
            head.previous = tail;
                

            head = newhead;
            tail = newtail;

            return newhead;
        }

(这假设您在 addLast() 中保持尾部更新。如果是,您也可以使用它。这就是我所拥有的:)

    public void addLast(Object inData)
    {
        Node node = new Node();
        node.next = null;
        node.data = inData;
        node.previous = tail;
        if (head == null) 
        { 
            head = node; 
        }
        if (tail != null)
        {
            tail.next = node;
        }
        tail = node;
    }

我希望这更清楚。

我认为您缺少的一件事是,每当您设置一个节点的Next属性时,您还应该设置相应的下一个节点的Previous属性。 这样做可能有助于列表导航。

否则,将任务分解为更谨慎的方法会很有帮助,因此一种方法不会发生太多事情。

例如,可能有用的一件事是添加一个返回特定索引处的节点的方法。 这使我们可以轻松地抓取一个节点并将其设置为新的Head ,这意味着我们的Rotate方法不需要循环 - 它只是获取适当索引处的节点并将该节点设置为新的头。

请注意,将节点设置为新的 Head 节点也应该分解为一个新方法,因为其中也涉及到相当多的内容(新的头意味着新的尾,并且需要考虑许多PreviousNext属性)。

希望这个基于您的代码的实现具有指导意义。 如果有什么不明白的,请告诉我:

public class Node
{
    public object Data { get; set; }
    public Node Next { get; set; }
    public Node Previous { get; set; }

    public Node(object data)
    {
        Data = data;
    }

    public override string ToString()
    {
        return Data.ToString();
    }
}

public class LinkedList 
{
    public Node Head { get; private set; }
    public Node Tail { get; private set; }
    public int Count
    {
        get
        {
            var count = 0;
            for (var n = Head; n != null; n = n.Next, count++) ;
            return count;
        }
    }

    private void AddFirst(object data)
    {
        var node = new Node(data) {Next = Head?.Next};
        if (Head != null) { Head.Previous = node;}
        if (Tail == null) Tail = node;
        Head = node;
    }

    public void Add(object data)
    {
        if (Head == null)
        {
            AddFirst(data);
        }
        else
        {
            var node = new Node(data) {Previous = Tail?.Previous};
            if (Tail != null) Tail.Next = node;
            node.Previous = Tail;
            Tail = node;
        }
    }

    private Node ItemAt(int index)
    {
        var item = Head;
        while (index-- > 0) item = item.Next;
        return item;
    }

    private void SetNewHead(int indexOfNewHead)
    {
        var newHead = ItemAt(indexOfNewHead);
        var newTail = newHead.Previous;
        var oldHead = Head;
        var oldTail = Tail;

        newHead.Previous = null;
        newTail.Next = null;
        oldTail.Next = oldHead;
        oldHead.Previous = oldTail;

        Head = newHead;
        Tail = newTail;
    }

    /// <summary>
    /// Rotates the Tail to the Head the specified number of times
    /// </summary>
    /// <param name="rotations">
    /// The number of times to rotate the Tail to the Head
    /// A negative number rotates the Head to the Tail
    /// </param>
    public void Rotate(int rotations)
    {
        var count = Count;
        rotations = rotations % count;
        if (rotations == 0) return;

        // Find the index of the new head based on the number of rotations
        // If rotations is positive, then the index is the count minus rotations
        // If rotations is negative, then the index is 'rotations * -1' (or -rotations) 
        var newHeadIndex = rotations > 0 ? count - rotations : -rotations;

        // Now that we know the index of the new head, we just set it as the head
        SetNewHead(newHeadIndex);
    }

    public List<Node> ToList()
    {
        var nodes = new List<Node>();
        for (var node = Head; node != null; node = node.Next)
        {
            nodes.Add(node);
        }
        return nodes;
    }

    public override string ToString()
    {
        return Count == 0 ? "{no items}" : "'" + string.Join("' <-> '", ToList()) + "'";
    }
}

这是一个示例程序,可让您输入一些数据然后旋转列表:

public static void Main(string[] args)
{
    var linkedList = new LinkedList();

    while (true)
    {
        var data = GetStringFromUser("Enter data to add, or 'exit' to stop: ");
        if (data.Equals("exit", StringComparison.OrdinalIgnoreCase))
            break;
        linkedList.Add(new Node(data));
    }

    Console.WriteLine("\nLinked list items:");
    Console.ForegroundColor = ConsoleColor.Green;
    Console.WriteLine(linkedList);
    Console.ResetColor();

    while (true)
    {
        var rotations = GetIntFromUser("\nEnter number of rotations, or 42 to stop: ");
        if (rotations == 42) break;
        linkedList.Rotate(rotations);
        Console.WriteLine($"\nLinked list items after {rotations} rotations:");
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine(linkedList);
        Console.ResetColor();
    }
}

上面的代码使用了以下辅助方法:

public static string GetStringFromUser(string prompt)
{
    Console.Write(prompt);
    return Console.ReadLine();
}

public static int GetIntFromUser(string prompt, Func<double, bool> validator = null)
{
    bool isValid = true;
    int result;

    do
    {
        if (!isValid)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Invalid input, please try again.");
            Console.ResetColor();
        }
        else isValid = false;

        Console.Write(prompt);
    } while (!int.TryParse(Console.ReadLine(), out result) &&
                (validator == null || !validator.Invoke(result)));

    return result;
}

样品 Output

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM