链表剖析之单链表剖析(二)

链表剖析之单链表剖析(二)代码依旧用c#的写的增加了修改删除合并的方法,我会坚持写下去,如有不对 之处希望大家斧正,注释写的比较详细。有什么问题欢迎留言。

链表节点类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 线性表ccc
{
    public class MyLinkListNode<T>
    {
        public MyLinkListNode()
        {
        
        }
        public T data;
        public MyLinkListNode(T data)
        {
            this.data = data;
        }
        //下一个节点
        public MyLinkListNode<T> next;

    }
}


链表类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 线性表ccc
{
    public class MyLinkList<T> 
    {
        //链表长度
       private int count;

        public int Count
        {
            get { return count; }
        }
        //链表头
        MyLinkListNode<T> head;
        public MyLinkList()
        {
            head = new MyLinkListNode<T>();
            head.next = null;
        }
        public MyLinkList(T[] nums):this()
        {
            AddItem(nums);
        }
        /// <summary>
        /// 增加一组数组到链表里
        /// </summary>
        /// <param name="nums"></param>
        private void AddItem(T[] nums)
        {
            foreach (T item in nums)
            {
                AddObject(item);
            }
        }
        /// <summary>
        /// 把一个节点加到头节点后面
        /// </summary>
        /// <param name="item"></param>
        private void AddObject(T item)
        {
            //这里给单链表开辟了表长的空间
            MyLinkListNode<T> p = new MyLinkListNode<T>();
            p.data = item;
            //设置原来的父节点的子节点为 现子节点的父节点
            p.next = head.next;
            //建立头节点与子节点的关系
            head.next = p;
            count++;
        }
        /// <summary>
        /// 获取链表某个位置的元素
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public T GetElem(int index)
        {
            MyLinkListNode<T> p = head.next;
            int j = 0;
            while(p!=null&&index>j)
            {
                p = p.next;
                j++;
            }
            if (p==null||j>index)
            {
                throw new Exception("超出界限");
            }
            return p.data;
        }
        /// <summary>
        /// 向链表插入一个元素
        /// </summary>
        /// <param name="index"></param>
        /// <param name="elem"></param>
        /// <returns></returns>
        public void InsertElem(int index,T elem)
        {
            MyLinkListNode<T> p = head;
            int j = 0;
            if (index>=count)
            {
                throw new Exception("插入的索引超界");
            }

            while (p != null&&index>j)
            {
                p = p.next;
                j++;
            }
            MyLinkListNode<T> newnode = new MyLinkListNode<T>(elem);
            //让它指向原来上一个节点指向的元素
            newnode.next = p.next;
            //让老节点指向插入的新节点
            p.next = newnode;
            count++;
        }
        /// <summary>
        /// 删除一个元素
        /// </summary>
        public void DeleteElem(int index)
        {
            //把头节点赋值给一个临时节点
            MyLinkListNode<T> p = head;
            int j = 0;
            // =的情况也不行因为链表最多取到count - 1
            if (index > count)
            {
                throw new Exception("删除的索引超界");
            }
            //取到删除节点的上一个节点
            while (p != null&&index > j)
            {
                p = p.next;
                j++;
            }
            //删除P.next让p.next指向p.next的next; 此时原来的p.next没有引用会被GC自动销毁
            p.next = p.next.next;
            //删除之后count要-1
            count--;
            //GC.Collect();
        }
        /// <summary>
        /// 修改链表index位置的值
        /// </summary>
        /// <param name="index"></param>
        /// <param name="elem"></param>
        public void UpDateElem(int index,T elem)
        {
            MyLinkListNode<T> p = head;
            // =的情况也不行因为链表最多取到count - 1
            if (index >= count)
            {
                throw new Exception("删除的索引超界");
            }
            int j = 0;
            //定位要要修改的位置
            while (index >= j)
            {
                p = p.next;
                j++;
            }
            p.data = elem;
        }
        /// <summary>
        /// 把La和Lb归并成一个单链表,由于不知道类型传一个回调函数进来
        /// </summary>T
        /// <param name="ListA"></param>
        /// <param name="ListB"></param>
        /// <returns></returns>
        public MyLinkList<T> MergeList(MyLinkList<T> La,Func<T,T,bool> func)
        {
            MyLinkList<T> Lc = new MyLinkList<T>();
            Lc.count = La.count + count;
            MyLinkListNode<T> pa, pb, pc;
            pb = La.head.next;
            pa = this.head.next;
            //Lc.head和pc指向同一片空间
            pc = Lc.head;
            while (pa != null && pb!=null)
            {
                //如果pa<=pb
                if (func(pa.data,pb.data))
                {
                    pc.next = pa;
                    //让pc指针前移
                    pc = pa;
                    //pa的指针前移
                    pa = pa.next;
                }
                else
                {
                    pc.next = pb;
                    pc = pb;
                    pb = pb.next;
                }
            }
            if (pb != null)
            {
                //1次就Pb后面链着的都归了pc
                pc.next = pb;
                //这时候pc的指针也得前移
                pc = pc.next;
            }
            else
            {
                pc.next = pa;
                pc = pc.next;
            }
            return Lc;
        }
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            MyLinkListNode<T> p = head.next;
            while (p != null)
            {
                sb.Append(p.data + " ");
                p = p.next;
            }
            return sb.ToString();
        }

    }
}

实际上操作 都是引用的各种变换 。

测试代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 线性表ccc
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("请输入测试数组类似5 4 3");
            string[] s = Console.ReadLine().Split(‘ ‘);
            int[] nums = new int[s.Length];
            for (int i = 0; i < s.Length; i++)
            {
                nums[i] = Convert.ToInt32(s[i]);
            }
            //初始化的时候存的类似于 3=>4=>5
            MyLinkList<int> ml = new MyLinkList<int>(nums);
            Console.WriteLine("链表输出的结果是");
            Console.WriteLine(ml.ToString());
            while (true)
            {
                Console.WriteLine("请输入插入的位置");
                int indexInsert = int.Parse(Console.ReadLine());
                Console.WriteLine("请输入插入的值");
                int value = int.Parse(Console.ReadLine());
                ml.InsertElem(indexInsert, value);
                Console.WriteLine("打印链表结果");
                Console.WriteLine(ml.ToString());
                Console.WriteLine("请输入要删除的索引");
                int indexDel = int.Parse(Console.ReadLine());
                ml.DeleteElem(indexDel);
                Console.WriteLine(ml.ToString());
                Console.WriteLine("请输入要修改的索引");
                int indexUpdate = int.Parse(Console.ReadLine());
                Console.WriteLine("请输入要修改的值");
                int valueUpdate = int.Parse(Console.ReadLine());
                ml.UpDateElem(indexUpdate, valueUpdate);
                Console.WriteLine("打印链表结果");
                Console.WriteLine(ml.ToString());
                Console.WriteLine("请输入链表B类似5 4 3");
                string[] sB = Console.ReadLine().Split(‘ ‘);
                int[] numsB = new int[sB.Length];
                for (int i = 0; i < s.Length; i++)
                {
                    numsB[i] = Convert.ToInt32(sB[i]);
                }
                MyLinkList<int> mlB = new MyLinkList<int>(numsB);
                MyLinkList<int> mlC = ml.MergeList(mlB, (x,y)=>x<=y);
                Console.WriteLine("合并两个链表的结果是");
                Console.WriteLine(mlC.ToString());
                Console.WriteLine();
            }
        }  
    }
}


结果如图:

链表剖析之单链表剖析(二)

算法分析:

删除要找到 删除节点的前一个节点 故一次循环时间复杂度是O(n)

修改也要找到删除节点 时间复杂度也是O(n)

合并操作一共循环m+n次 时间复杂度是O(m+n) 即O(n)

最后更正一下上一篇的一个错误 遍历的时间复杂度是O(n) 不需要循环GetElem详细代码 请参考 这篇里MyLinkList 重载的ToString()方法


 

链表剖析之单链表剖析(二)

上一篇:POJ 2152 经典树形dp


下一篇:Codeforces Round #228 (Div. 2)