C#集合遍历时删除和增加元素的方法

 更新时间:2020年6月25日 11:23  点击:1641

本文实例讲述了C#集合遍历时删除和增加元素的方法。分享给大家供大家参考,具体如下:

大多数时候,遍历集合元素的时候并不需要对元素进行增加或者删除操作,但有些时候则需要,比如,如果集合中盛放的元素是社会上所有的人,那么有人死亡则元素删除,有人出生则是集合元素的增加。对于这种情况,遍历不能按照原来那种方式去做了,而且C#中的集合对于这类有增删动作的遍历,也不支持foreach循环。

有三种办法可以解决这一问题。

第一种方法:使用C#的LinkedList<>双链表。我原来设想,把原来链表需要删除的元素直接remove掉,那些新添加的元素,先装入到一个临时链表中,等循环结束,再用Add把临时链表的头结点添加到原来链表的尾部即可,这样算法的复杂度也较低,但是,出乎意料的是,C#的双链表,无法将属于另外一个链表的结点添加到本链表中,其Next属性也只读。无奈,只能一边循环,一边在原链表尾端添加结点,这样就需要标记处循环结束的位置,即需要在原来的未改动的链表的尾部结点处结束循环,而不是在改动后的链表的尾部结点处结束。这样就要求在循环开始之前,先获得尾部结点的引用。程序如下(链表中有0-29的整数值结点,遍历时遇到3的整数倍,就在链表尾端添加一个0值结点,遇到2的整数倍就删除结点)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace 集合遍历时删除或增加元素
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }
    private LinkedList<int> list = new LinkedList<int>();
    //初始化,添加0-29的整数进入链表
    private void button1_Click(object sender, EventArgs e)
    {
      for (int i = 0; i < 30; i++)
      {
        this.list.AddLast(i);
      }
    }
    //遍历链表,做如下操作:
    //遇到能被3整除的,就在该链表后增加一个0元素,遇到能被2整除的就删除该元素
    private void button2_Click(object sender, EventArgs e)
    {
      LinkedListNode<int> nodeNow = this.list.First;//链表第一个元素
      LinkedListNode<int> nodeLast = this.list.Last;//原链表的最后一个元素,循环结束的标记
      LinkedListNode<int> nodeTmp;//临时结点
      //循环结束的条件是,等当前结点是原链表的最后一个结点
      while (nodeNow != nodeLast)
      {
        //如果能被3整除时,则在链表后加一个0
        if (nodeNow.Value % 3 == 0)
        {
          this.list.AddLast(0);
        }
        //如果能被2整除,则删除该元素
        if (nodeNow.Value % 2 == 0)
        {
          //如果nodeNow被删除了,那么一定不能用Next获取下一个要判断的元素
          //因为已经自动向下一个移动了,这是就要在删除nodeNow之前,
          //获取它的Next,赋给nodeTmp,待nodeNow删除之后,再把nodeTmp的内存赋给nodeNow
          nodeTmp = nodeNow.Next;
          this.list.Remove(nodeNow);
          nodeNow = nodeTmp;
        }
        else
        {
          //如果不能被2整除,则在链表中保留该元素,并获得下一个并进行判断
          nodeNow = nodeNow.Next;
        }
      }
      //最后不要忘记对nodeLast(原链表最后一个元素)本身进行处理,上面的while循环没有包括这个nodeLast
      if (nodeNow.Value % 3 == 0)
      {
        this.list.AddLast(0);
      }
      if (nodeNow.Value % 2 == 0)
      {
        this.list.Remove(nodeNow);
      }
    }
    //测试结果
    private void button3_Click(object sender, EventArgs e)
    {
      foreach (int i in this.list)
      {
        Console.WriteLine(i);
      }
    }
  }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace 集合遍历时删除或增加元素
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }
    private LinkedList<int> list = new LinkedList<int>();
    //初始化,添加0-29的整数进入链表
    private void button1_Click(object sender, EventArgs e)
    {
      for (int i = 0; i < 30; i++)
      {
        this.list.AddLast(i);
      }
    }
    //遍历链表,做如下操作:
    //遇到能被3整除的,就在该链表后增加一个0元素,遇到能被2整除的就删除该元素
    private void button2_Click(object sender, EventArgs e)
    {
      LinkedListNode<int> nodeNow = this.list.First;//链表第一个元素
      LinkedListNode<int> nodeLast = this.list.Last;//原链表的最后一个元素,循环结束的标记
      LinkedListNode<int> nodeTmp;//临时结点
      //循环结束的条件是,等当前结点是原链表的最后一个结点
      while (nodeNow != nodeLast)
      {
        //如果能被3整除时,则在链表后加一个0
        if (nodeNow.Value % 3 == 0)
        {
          this.list.AddLast(0);
        }
        //如果能被2整除,则删除该元素
        if (nodeNow.Value % 2 == 0)
        {
          //如果nodeNow被删除了,那么一定不能用Next获取下一个要判断的元素
          //因为已经自动向下一个移动了,这是就要在删除nodeNow之前,
          //获取它的Next,赋给nodeTmp,待nodeNow删除之后,再把nodeTmp的内存赋给nodeNow
          nodeTmp = nodeNow.Next;
          this.list.Remove(nodeNow);
          nodeNow = nodeTmp;
        }
        else
        {
          //如果不能被2整除,则在链表中保留该元素,并获得下一个并进行判断
          nodeNow = nodeNow.Next;
        }
      }
      //最后不要忘记对nodeLast(原链表最后一个元素)本身进行处理,上面的while循环没有包括这个nodeLast
      if (nodeNow.Value % 3 == 0)
      {
        this.list.AddLast(0);
      }
      if (nodeNow.Value % 2 == 0)
      {
        this.list.Remove(nodeNow);
      }
    }
    //测试结果
    private void button3_Click(object sender, EventArgs e)
    {
      foreach (int i in this.list)
      {
        Console.WriteLine(i);
      }
    }
  }
}

第二种方法:使用C#的List<>,List<>是基于数组的顺序表,增加、删除动作时间复杂度较高,不如链表的效率高。其基本原来同第一种方法相似,也需要使用一个int型的变量标记原顺序表的尾部元素,当删除一个元素时,这个变量需要自减。代码略。

第三种方法,自定义单链表泛型类(链表类见https://www.jb51.net/article/87610.htm)。跟第一种方法比的好处,就是能够灵活实现两个链表的合并,只需要把第二个链表的头结点设置成第一个链表的尾结点的Next的结点(或直接Add)就可以了。其实对于C#的双链表,我并不是很清楚,为什么AddLast()方法,无法将一个链表的元素添加到另一个链表中,而只能添加一个不属于任何链表的结点(有人说第一种方法,其实可以使用结点Clone,但是这样无非还是增加算法的空间和时间复杂度,违背了使用链表的本意)。C#之所以不支持这种做法的原因可能是,MS担心你加入的结点,位于一个环状链表上,这样会导致原链表的Last属性、Count属性等无法计算(形成死循环)。测试代码如下:

//两个链表的合并
LinkedList<int> list = new LinkedList<int>();
for (int i = 0; i < 10; i++)
{
   list.Add(i);
}
LinkedList<int> list2 = new LinkedList<int>();
for (int i = 10; i < 20; i++)
{
   list2.Add(i);
}
list.Add(list2.Head);
Node<int> n = list.Head;
while(n!=null)
{
   Console.WriteLine(n.Data);
   n = n.Next;
}
Console.ReadLine();
Console.WriteLine(list.GetLength());
Console.ReadLine();
//两个链表的合并
LinkedList<int> list = new LinkedList<int>();
for (int i = 0; i < 10; i++)
{
 list.Add(i);
}
LinkedList<int> list2 = new LinkedList<int>();
for (int i = 10; i < 20; i++)
{
 list2.Add(i);
}
list.Add(list2.Head);
Node<int> n = list.Head;
while(n!=null)
{
 Console.WriteLine(n.Data);
 n = n.Next;
}
Console.ReadLine();
Console.WriteLine(list.GetLength());
Console.ReadLine();

更多关于C#相关内容感兴趣的读者可查看本站专题:《C#遍历算法与技巧总结》、《C#程序设计之线程使用技巧总结》、《C#操作Excel技巧总结》、《C#中XML文件操作技巧汇总》、《C#常见控件用法教程》、《WinForm控件用法总结》、《C#数据结构与算法教程》、《C#数组操作技巧总结》及《C#面向对象程序设计入门教程》

希望本文所述对大家C#程序设计有所帮助。

[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25