0%

Prism的核心功能之一就是支持模块化应用程序开发(Modular Application Development),并且在运行时对各个模块进行动态管理。

使用Prism进行模块化开发首先要了解几个概念:

1.Module: Module是一些逻辑上相关的程序集或者资源文件的集合,在Silverlight程序中通常以xap文件为单位存在。而每一个Module中都需要有一个负责进行初始化工作以及与系统进行集成的角色,它需要实现IModule接口。IModule接口中只有一个Initialize方法,一方面这个接口将这个工程标记为一个Module,另一方面你可以在Initialize方法中实现一些逻辑,比如向容器中注册一些Service,或者将视图集成到程序中等等。

2.ModuleInfo: 在创建了一个Module之后,需要通知Prism这个Module的存在,也就是要注册一下。在Prism中,Module是以ModuleInfo的形式存在的。ModuleInfo记录了Module的信息,ModuleName属性是Module的标识符,相当于Module的ID;ModuleType是Module的AssemblyQualifiedName;DependsOn属性是该Module依赖的其它Module的ModuleName的集合,在加载该Module时,如果有依赖项没有加载的话,会先将依赖项加载;InitializationMode,有两种情况——WhenAvailable和OnDemand,当选择了WhenAvailable时,该Module会在程序启动时自动加载,如果选择了OnDemand,则会按需加载,默认情况下是WhenAvailable;Ref,存储该Module的位置,如XXX.xap;State,定义了Module从注册到加载到初始化的整个过程中的状态。

3.ModuleCatalog: ModuleCatalog实现了IModuleCatalog接口,它是ModuleInfo的容器,保存着系统中所有Module的信息,不仅会管理哪些Module需要加载,什么时候加载以什么顺序加载等问题,还要检查各个Module之间是否存在着循环依赖、是否有重复的Module等等。ModuleCatalog提供了含参构造方法和AddModule方法,可以通过代码将Module注册进去,同时也可以在xaml文件中配置好Module,然后通过ModuleCatalog.CreateFromXaml方法来加载。

4.ModuleManager: ModuleManager实现了IModuleManager接口。顾名思义就是管理Module的类。IModuleManager中含有两个方法和两个事件:Run方法会将所有InitializationMode为WhenAvailable的Module加载,然后进行初始化,初始化的工作委托给了IModuleInitializer来完成,它会获取到Module类(上面提到的实现了IModule接口的类)的实例,然后调用其Initialize方法。LoadModule方法用来加载InitializationMode为OnDemand的Module。两个事件分别用来通知下载Module的进度变化以及Module加载完成。

下面用一个示例程序来说明如何在Prism中进行模块化程序开发。

1.创建一个Silverlight Application,叫做PrismModule。

2.在Solution中添加三个Silverlight Application,分别叫做ModuleA, ModuleB, ModuleC。然后删除这三个工程中的App文件和MainPage文件。

3.在ModuleA工程下添加一个UserControl,叫做ViewA,然后再添加一个类,叫做ModuleA。并添加Microsoft.Practices.Prism和Microsoft.Practices.ServiceLocation引用。下面是ViewA和ModuleA的代码:

1
2
3
4
5
6
7
8
9
10
11
12
<UserControl x:Class="ModuleA.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">

<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="Module A" FontSize="22" />
</Grid>
</UserControl>
1
2
3
4
5
6
public class ModuleA : IModule
{
public void Initialize()
{
}
}

4.对ModuleB和ModuleC重复做步骤3的操作,只是将文本改成相应模块。

5.在PrismModule中添加对ModuleA、ModuleB、ModuleC、Prism、UnityExtensions还有Unity for Silverlight的引用,然后创建Shell和Bootstrapper。添加一个UserControl,叫做Shell;再添加一个类,叫做Bootstrapper。

Shell代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<UserControl x:Class="PrismModule.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://www.codeplex.com/prism"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800">

<StackPanel Margin="50">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Border VerticalAlignment="Top" BorderBrush="Red" BorderThickness="2" Width="200" Height="100">
<ContentControl prism:RegionManager.RegionName="RegionA" />
</Border>
<Border VerticalAlignment="Top" BorderBrush="Red" BorderThickness="2" Width="200" Height="100">
<ContentControl prism:RegionManager.RegionName="RegionB" />
</Border>
<StackPanel>
<Border BorderBrush="Red" BorderThickness="2" Width="200" Height="100">
<ContentControl prism:RegionManager.RegionName="RegionC" />
</Border>
<Button Content="Load Module C" Click="LoadModuleC" Width="120" Height="25" />
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

Shell.xaml.cs代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<UserControl x:Class="PrismModule.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://www.codeplex.com/prism"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800">

<StackPanel Margin="50">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Border VerticalAlignment="Top" BorderBrush="Red" BorderThickness="2" Width="200" Height="100">
<ContentControl prism:RegionManager.RegionName="RegionA" />
</Border>
<Border VerticalAlignment="Top" BorderBrush="Red" BorderThickness="2" Width="200" Height="100">
<ContentControl prism:RegionManager.RegionName="RegionB" />
</Border>
<StackPanel>
<Border BorderBrush="Red" BorderThickness="2" Width="200" Height="100">
<ContentControl prism:RegionManager.RegionName="RegionC" />
</Border>
<Button Content="Load Module C" Click="LoadModuleC" Width="120" Height="25" />
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

Bootstrapper代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return this.Container.TryResolve<Shell>();
}

protected override void InitializeShell()
{
App.Current.RootVisual = (UIElement)this.Shell;
}

protected override IModuleCatalog CreateModuleCatalog()
{
return new ModuleCatalog();
}

protected override void ConfigureModuleCatalog()
{
Type typeA = typeof(ModuleA.ModuleA);
ModuleInfo moduleA = new ModuleInfo
{ // ModuleA没有设置InitializationMode,默认为WhenAvailable
ModuleName = typeA.Name,
ModuleType = typeA.AssemblyQualifiedName,
};

Type typeB = typeof(ModuleB.ModuleB);
ModuleInfo moduleB = new ModuleInfo
{
ModuleName = typeB.Name,
ModuleType = typeB.AssemblyQualifiedName,
InitializationMode = InitializationMode.OnDemand,
};

Type typeC = typeof(ModuleC.ModuleC);
ModuleInfo moduleC = new ModuleInfo
{
ModuleName = typeC.Name,
ModuleType = typeC.AssemblyQualifiedName,
InitializationMode = InitializationMode.OnDemand,
// ModuleC依赖于ModuleB
DependsOn = new Collection<string> { moduleB.ModuleName },
};

this.ModuleCatalog.AddModule(moduleA);
this.ModuleCatalog.AddModule(moduleB);
this.ModuleCatalog.AddModule(moduleC);
}
}

将App.xaml.cs中的Application_Startup方法改为

1
2
3
4
5
private void Application_Startup(object sender, StartupEventArgs e)
{
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}

6.现在已经有了Region,需要将各个Module中的View填充到Region中。修改ModuleA,ModuleB和ModuleC的Initialize方法。

1
2
3
4
5
public void Initialize()
{
ServiceLocator.Current.GetInstance<IRegionManager>().
RegisterViewWithRegion("RegionA", typeof(ViewA));
}

将其中的A改为相应的字母。运行程序,结果如下:

image

我们点击按钮来加载ModuleC,因为ModuleC依赖于ModuleB,所以ModuleB也一块儿加载出来了。但是这与我们预期的效果不太一致。因为一共只load了一个xap文件,用WinRAR打开看一下,发现三个Module的程序集都在其中。

image

在Silverlight程序中,模块化程序开发应该不仅仅体现在开发时的模块化,运行时也应该是模块化的。比如ModuleA在程序加载时就load出来,但是ModuleB和ModuleC则是在点击了按钮后才load出来的,换句话说,在没点按钮前就不应该将ModuleB和ModuleC的程序集加载进来。现在由于PrismModule项目引用了三个Module,所以程序集会被一块打包进xap文件中。我们修改一下,将对ModuleB和ModuleC的引用的Copy Local属性设置为false:

imageimage

重新编译一下,再次查看xap文件,发现已经没有了ModuleB和ModuleC。

image

运行程序,报错。很简单,因为我们在Bootstrapper中用到了ModuleB和ModuleC,缺少了这两个dll,程序没法运行。为了解决这个问题,我们把初始化ModuleCatalog的过程改一下,不使用代码,而是使用配置文件。在Silverlight中,Prism支持使用xaml文件作为配置文件。下面在PrismModule工程下新建一个资源文件,ModuleCatalog.xaml。内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">

<Modularity:ModuleInfo Ref="ModuleA.xap" ModuleName="ModuleA"
ModuleType="ModuleA.ModuleA, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

<Modularity:ModuleInfo Ref="ModuleB.xap" ModuleName="ModuleB" InitializationMode="OnDemand"
ModuleType="ModuleB.ModuleB, ModuleB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

<Modularity:ModuleInfo Ref="ModuleC.xap" ModuleName="ModuleC" InitializationMode="OnDemand"
ModuleType="ModuleC.ModuleC, ModuleC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<Modularity:ModuleInfo.DependsOn>
<sys:String>ModuleB</sys:String>
</Modularity:ModuleInfo.DependsOn>
</Modularity:ModuleInfo>
</Modularity:ModuleCatalog>

这里大体和用代码写一致,只不过Ref属性里要写明该Module对应的是哪个xap包。Prism在Silverlight程序中使用一个叫做XapModuleTypeLoader的类来加载Module,在将Module下载之后会获取AppManifest.xaml文件,也就是说如果你的Module是个类库工程的话,会在加载时产生错误。可以将几个类库的程序集文件包装在一个xap文件中作为一个Module来使用,或者自定义一个ModuleTypeLoader。

定义完Module的配置文件后,要改写Bootstrapper。首先删除用代码配置Module的方法ConfigureModuleCatalog,然后在CreateModuleCatalog方法中替换成一下内容:

1
2
3
4
5
protected override IModuleCatalog CreateModuleCatalog()
{
return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(
new Uri("/PrismModule;component/ModuleCatalog.xaml", UriKind.Relative));
}

再次运行程序,正常运行。

image

这样就达到了按需加载的目的。节约带宽是一个好处,如果产品是分模块往外卖的时候,可以由客户按需定制。

不过再打开ModuleB和ModuleC的xap文件看一下,发现里面不仅有Module本身的程序集,还包括了引用的Prism的程序集等。而这些程序集其实已经在PrismModule.xap中包含了。完全没有必要重复下载。所以可以将多余的程序集的引用的Copy Local属性设置为false,这样就瘦身成功了。(想要避免重复加载相同的文件,也可以通过在项目的Properties面板中勾选Reduce XAP size by using application library caching选项)

如果你对Module的加载到执行的整个过程感兴趣,那么Prism本身提供了一个QuickStart,既有Unity版本也有Mef版本,不要错过。

在程序中使用框架必然要有一个切入点,框架会在这里进行初始化,处理相关配置信息等。在Prism中扮演这一角色的就是Bootstrapper。

Prism提供了一个抽象基类Bootstrapper,这个类里面包含了包含了许多空的虚方法,可以重写它们添加自己的逻辑。这个基类与任何容器无关,所以可以通过继承它来实现基于特定容器的Bootstrapper,不过通常我们大可不必这样做,因为Prism默认提供了两个基于特定容器的Bootstrapper——UnityBootstrapper和MefBootstrapper,分别使用Unity和Mef来实现依赖注入。而我们需要做的工作就是在这两个类之间选择一个适合自己的,稍微配置一下就可以了。当然如果你不喜欢这两个容器或者已有的程序使用了其它容器(如Spring.Net, Castle等),也可以通过继承Boostrapper抽象基类来实现自己的SpringBootstrapper和CastleBootstrapper。虽然UnityBootstrapper的代码看起来挺简单的,但是如果仿照这个来实现CastleBootstrapper却并不是那么容易的一件事(不信你可以试试),所以更好的办法是用现成的。

那么Bootstrapper都做了些什么呢?
  1. 创建Logger:

    执行CreateLogger方法,默认创建一个EmptyLogger,不会在任何地方输出log。当然是可以扩展的,比如你可以使用Clog来做一个适配器。

  2. 创建并配置ModuleCatalog

执行CreateModuleCatalog方法,默认创建一个空的ModuleCatalog。然后执行ConfigureModuleCatalog方法,默认情况下这个方法是空的。可以重写这两个方法,加入自定义的获取ModuleCatalog的逻辑,比如在CreateModuleCatalog中可以从一个xaml文件中读取Module信息。

1
2
3
4
5
protected override IModuleCatalog CreateModuleCatalog()
{
return ModuleCatalog.CreateFromXaml(new Uri("/AssemblyName;component/ModulesCatalog.xaml", UriKind.Relative));
}

  1. 创建并配置依赖注入容器

Prism中使用依赖注入来管理各个组件,你可以使用任何你熟悉的容器,比如Castle, Unity等。Prism中内置了对Unity以及Mef的支持,所以有两种预定义好的Bootstrapper: UnityBootstrapper和MefBootstrapper,其中分别采用UnityContainer和CompositionContainer作为依赖注入容器。以UnityBootstrapper为例,在这一步中会先调用CreateContainer方法,返回一个UnityContainer;然后调用ConfigureContainer方法,在这个方法中主要是将一些常用的类注册到容器中。

  1. 配置默认的Region适配器映射

为了使xaml中的UI控件可以使用Region,需要先注册一下。Prism默认支持Region的控件类型有:TabControl, Selector, ItemsControl, ContentControl。当然你也可以通过实现IRegionAdapter接口或者直接继承RegionAdapterBase来使其它控件也支持Region。

  1. 配置默认的Region 行为(Behavior)

为RegionBehaviorFactory添加一些默认的行为。这样可以扩展Region的行为。可以通过实现IRegionBehavior接口或继承RegionBehavior来自定义Region的行为,并重写ConfigureDefaultRegionBehaviors方法添加到Region。

  1. 注册框架异常类型

Prism提供了ExceptionExtensions类来帮助开发人员定位异常发生的根异常。在这一步通过调用RegisterFrameworkExceptionTypes方法向ExceptionExtensions中添加新的Root Exception。

  1. 创建并初始化Shell

首先调用CreateShell方法来创建一个Shell,这是一个抽象方法,通常这个方法中就是返回作为整个网站容器的页面。之后会将RegionManager attach到Shell上,然后更新定义的Regions,最后调用InitializeShell方法来初始化Shell。默认情况下这是个空方法,可以通过重写这个方法加入自定义的逻辑,可以在这个方法中将Shell作为Silverlight程序的根容器页面显示出来。

1
2
3
4
5
protected override void InitializeShell()`
{
Application.Current.RootVisual = Shell;
}

  1. 初始化Modules

调用InitializeModules方法,实际上就是调用ModuleManager.Run方法,会调用ModuleCatalog中的所有InitializationMode为WhenAvailable的Module的Initialize方法。

至此,整个容器的初始化过程就完毕了。

值得一提的还有CommonServiceLocator,这同样是Patterns & Practices小组的产品。它的作用很简单,就是统一依赖注入容器的接口,使程序不必依赖于特定的容器,只需要使用ServiceLocator,然后去间接地使用其它各种各样的容器。在Prism的内部就是使用ServiceLocator来进行管理的。所以不管使用什么样的容器,都需要提供一个实现了IServiceLocator接口的适配器,如使用Unity要提供UnityServiceLocatorAdapter,使用Mef要提供MefServiceLocatorAdapter。这样不管外部使用什么容器,内部都不需要改变。所以如果要使用Prism重头开始构架一个程序,那么在整个程序中不依赖于特定的依赖注入容器接口,而是使用ServiceLocator是一个不错的选择,这样可以在需要的情况下很容易地更换容器,只需要重写一个Bootstrapper和一个ServiceLocatorAdapter就可以了。

Prism是由微软Patterns & Practices团队开发的项目,目的在于帮助开发人员构建松散耦合的、更灵活、更易于维护并且更易于测试的WPF应用或是Silverlight应用以及Windows Phone 7应用。使用Prism可以使程序开发更趋于模块化,整个项目将由多个离散的、松耦合的模块组成,而各个模块又可以又不同的开发者或团队进行开发、测试和部署。目前Prism的最新版本是Prism 4,于2010年11月12日发布。Prism有很完整的文档以及丰富的示例程序。在这里我们仅针对于Silverlight程序的开发。

在下载Prism安装包并安装完成后,会在目标文件夹中发现很多文件。

推荐首先运行RegisterPrismBinaries.bat文件,这样在开发基于Prism的程序时可以更方便地添加引用程序集。

使用Prism之前,需要了解一些概念,下面通过一个非常简单的小程序来了解一下Prism。

1.打开Visual Studio 2010,新建一个Silverlight Application项目,并添加对Prism的引用。再创建三个Silverlight类库工程。

2.在Contract工程下新建一个接口,叫做ITextProvider。

1
2
3
4
5

public interface ITextProvider
{
string GetText();
}

3.在其它的三个项目中都引用Contract项目。

4.在PrismStarter工程下新建一个TextProvider类并实现ITextProvider接口。

1
2
3
4
5
6
7
8
9
10
11
public class TextProvider : ITextProvider
{
private int i = 0;

public string GetText()
{
i++;
return string.Format("From TextProvider [{0}]", i);
}
}

5.删除PrismStarter项目中自动生成的MainPage.xaml,创建一个新的UserControl,叫做Shell。页面代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<UserControl x:Class="PrismStarter.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://www.codeplex.com/prism"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">

<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>

<TextBlock FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center" Text="Prism Starter" />

<ContentControl Grid.Row="1" HorizontalContentAlignment="Stretch" prism:RegionManager.RegionName="RegionA" />

<ContentControl Grid.Row="2" HorizontalContentAlignment="Stretch" prism:RegionManager.RegionName="RegionB" />
</Grid>
</UserControl>

6.在ModuleA工程中添加对Prism程序集的引用。并添加一个UserControl叫做ViewA,页面代码为:

1
2
3
<Grid :Name="LayoutRoot" Background="White">
<TextBlock x:Name="textModuleA" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>

CodeBehind中的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
public partial class ViewA : UserControl
{
public ViewA(ITextProvider textProvider)
{
InitializeComponent();

this.Loaded += (s, e) =>
{
textModuleA.Text = string.Format("Module A {0}", textProvider.GetText());
};
}
}

7.在ModuleA工程中添加一个类叫做ModuleA,并实现接口IModule。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ModuleA : IModule
{
private IRegionManager _regionManager;

public ModuleA(IRegionManager regionManager)
{
_regionManager = regionManager;
}

public void Initialize()
{
_regionManager.RegisterViewWithRegion("RegionA", typeof(ViewA));
}
}

注意这里的RegionA对应于Shell页面中的RegionName。

8.在ModuleB工程中重复6、7过程,只是将A替换为B。

9.在PrismStarter工程中添加对ModuleA和ModuleB的引用。

10.在PrismStarter工程中添加一个PrismStarterBootstrapper类,并继承UnityBootstrapper。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class PrismStarterBootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return this.Container.TryResolve<Shell>();
}

protected override void InitializeShell()
{ // 控制页面在初始化时显示Shell页面
App.Current.RootVisual = (UIElement)this.Shell;
}

protected override void ConfigureModuleCatalog()
{ // 注册Module。在实际开发中可以使用xaml做配置文件,
// 这样就可以将PrismStarter与ModuleA和ModuleB完全解耦,也就不再需要引用这两个项目
Type moduleAType = typeof(ModuleA.ModuleA);
ModuleInfo moduleA = new ModuleInfo
{
ModuleName = moduleAType.Name,
ModuleType = moduleAType.AssemblyQualifiedName,
};

Type moduleBType = typeof(ModuleB.ModuleB);
ModuleInfo moduleB = new ModuleInfo
{
ModuleName = moduleBType.Name,
ModuleType = moduleBType.AssemblyQualifiedName,
};

this.ModuleCatalog.AddModule(moduleA);
this.ModuleCatalog.AddModule(moduleB);
}

protected override void ConfigureContainer()
{ // 注册一下TextProvider,这样在通过容器请求ITextProvider时会返回TextProvider实例
base.ConfigureContainer();
this.Container.RegisterInstance<ITextProvider>(new TextProvider());
}
}

11.最后一步,打开App.xaml.cs,修改Application_Startup方法

1
2
3
4
5
private void Application_Startup(object sender, StartupEventArgs e)
{
PrismStarterBootstrapper bootstrapper = new PrismStarterBootstrapper();
bootstrapper.Run();
}

运行程序,结果如下:

image

下面简单介绍一下这个小例子中涉及到的一些概念。

Bootstrapper: 在程序中使用框架需要找到一个切入点,将框架植入进去,将一部分功能委托给框架来实现。在Silverlight中使用Prism的切入点就是App.xaml.cs中的Application_Startup方法。一般来说,这个方法中只是指定页面最先加载的页面,但是我们把默认的逻辑去掉,取而代之的是Bootstrapper(在本例中就是PrismStarterBootstrapper)。当调用Bootstrapper.Run方法时,它会完成一些准备工作,如一些配置等。因此你会发现,使用Prism后,启动程序时会比正常启动要慢一些,就是因为Bootstrapper做了许多工作。

Container: 依赖注入容器。在程序中使用依赖注入的好处到处都可以找的到。在Silverlight中使用容器来管理各个组件的一个很明显的好处就是使用单例来降低内存使用。否则每次加载一个页面都需要重新创建一个也很耗费资源的。当然好处不只这些,通过容器来注入一些服务(如本例中的IRegionManager和ITextProvider)显得相当方便。

Module: Prism帮助我们把程序分解成一个个功能模块,这些功能模块就叫做Module,通常一个工程就是一个Module。由于Module彼此是独立的,但是在运行时需要将它们整合到一起,因此Prism需要知道Module的存在,这里就涉及到了ModuleCatalog, ModuleCatalog就是Module的容器,里面包含了所有Module的信息,以ModuleInfo的形式存在。ModuleInfo就是对Module的抽象,包含Module的名字,类型,依赖等一些信息。

Shell: 相当于程序的入口,初始界面,还能够提供类似ASP.Net中的母版页的功能。Shell必须由Bootstrapper创建,因为Shell需要使用的一些service,比如RegionManager等,需要在Shell显示前注册。

Region: 相当于ASP.Net中的ContentPlaceHolder(是这么叫的吧?),起到占位符的作用,如本例中Shell中有两个Region——RegionA和RegionB,定义了两块区域。在Module的初始化过程中,通过IRegionManager将Module中的页面放进了定义好的Region中。IRegionManager负责管理Region,可以通过它向Region中注册View,进行导航等。

Prism的功能当然远不止这么简单,它还提供对MVVM模式的支持,对导航的支持等,在后续文章中会逐步介绍。希望能够通过本文让大家对Prism有一定的了解。

代码下载

DotNet

C#开源系统大汇总 - 心存善念 - 博客园

Npoi操作excel - 张龙豪 - 博客园

.NET Core 跨平台发布(dotnet publish) - LineZero - 博客园

如何用.net c# 读取epub格式文件 - szliszt的专栏 - 博客频道 - CSDN.NET

使用epublib自动生成epub文件 - zhyoulun的专栏 - 博客频道 - CSDN.NET

将Log4net的配置配置到的独立文件中 - 422159763 - 博客园

AutoMapper小结 - 呆河马 - 博客园

AutoMapper完成Dto与Model的转换 - kuangkro - 博客园

Quartz+TopShelf实现Windows服务作业调度 - Frozen.Zhang - 博客园

Windows服务调用Quartz.net 实现消息调度 - M.Zero - 博客园

WebApi:WebApi的Self Host模式 - MAQNH - 博客园

【MVC】ASP.NET MVC Forms验证机制 - bomo - 博客园

【WEB API项目实战干货系列】- 导航篇(十足干货分享) - DukeCheng - 博客园

ASP.NET MVC中Autofac实现的自动注入模式 - Nic Pei - 博客园

MVC5+Unity4.0注入依赖学习 - 灵猪谁仰的专栏 - 博客频道 - CSDN.NET

Unity框架的依赖注入-Dependency injection - 徐某人 - 博客园

C#软件license管理(简单软件注册机制) - CSDN博客

动手写C#注册码工具(提供源码) - 唯吴独尊 - 博客园

授权组件设计 - LicenseControlProject - Cracker - 博客园

ERP框架开发中的License许可验证机制设计与实现 (包含源代码下载) - 信息化建设 - 博客园

.NET分离exe和dll在不同的目录让你的程序更整洁 - .NET快速开发框架 - 博客园

.net RPC框架选型 - 凌晨三点半 - 博客园

[C#进阶系列]专题一:深入解析深拷贝和浅拷贝 - Learning hard - 博客园

通用的序列号生成器库 - 张善友 - 博客园

根据twitter的snowflake算法生成唯一ID - 梁照彬 - 博客园

C#动态调用WCF接口,两种方式任你选。 - Danny Chen - 博客园

WCF 学习笔记: ServiceHost - CSDN博客

使用ASP.Net WebAPI构建REST服务(六)——Self-Host - 天方 - 博客园

构建基于Chromium的应用程序 - 猫不理饼 - 博客园

8种主要排序算法的C#实现 - 胖鸟低飞 - 博客园

C#位运算 - Danny Chen - 博客园

快速排序算法(C#实现) - Eric Sun - 博客园

冒泡排序算法(C#实现) - Eric Sun - 博客园

归并排序算法(C#实现) - Eric Sun - 博客园

堆排序算法(C#实现) - Eric Sun - 博客园

插入排序算法–直接插入算法,折半排序算法,希尔排序算法(C#实现) - Eric Sun - 博客园

C# 经典排序算法大全 - 张世辉 - CSDN博客

C#排序算法小结 - hungerW - 博客园

C#排序算法的比较 - r163 - 博客园

ASP.NET MVC使用Oauth2.0实现身份验证 - 冰碟 - 博客园

Asp.Net MVC 4 Web API 中的安全认证-使用OAuth - Nic Pei - 博客园

使用Owin中间件搭建OAuth2.0认证授权服务器 - CSDN博客

使用DotNetOpenAuth搭建OAuth2.0授权框架 - 莱布尼茨 - 博客园

ASP.NET WebApi OWIN 实现 OAuth 2.0 - 田园里的蟋蟀 - 博客园

asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code) - 蓝建荣 - 博客园

[OAuth]基于DotNetOpenAuth实现Client Credentials Grant - dudu - 博客园

.net 单点登录实践 - 何雪峰 - 博客园

Personball’s Blog - 使用Owin中间件搭建OAuth2.0认证授权服务器

如何给你的ASP.NET页面添加HelpPage - pmars - 博客园

ASP.NET Identity登录原理 - Claims-based认证和OWIN - Jesse Liu - 推酷

如何使用FluentMigrator进行数据库迁移 - LamondLu - 博客园

领域驱动设计 - 随笔分类 - dax.net - 博客园

C# CacheHelper - Ariter - 博客园

Memcache升级版:CouchBase的安装配置与使用说明_PHP教程_开源小组_开源社区

如何用C#动态编译、执行代码 - Danny Chen - 博客园

Repository模式介绍汇总 - 跟着阿笨一起玩.NET - 博客园

8天掌握EF的Code First开发系列之5 视图、存储过程和异步API - JustYong - 博客园

1.【使用EF Code-First方式和Fluent API来探讨EF中的关系】 - 灰太狼的梦想 - 博客园

EF批量操作数据与缓存扩展框架 - 天使不哭 - 博客园

献给Fluent NHibernate的初学者 - Nic Pei - 博客园

6.数据访问技术 - 随笔分类 - Apollo.NET - 博客园

EF架构~系列目录 - 张占岭 - 博客园

Entity Framework Code First (五)Fluent API - 配置关系 - 舍长 - 博客园

wpf 实现实时毛玻璃(live blur)效果 - CSDN博客

WPF 几行代码实现窗体毛玻璃效果(Aero Glass) - CSDN博客

Wpf开源收集 - 刺客mrchenzh - 博客园

WPF开源界面库 - iGotogo - 博客园

[Prism框架实用分享]如何在主程序中合理的弹出子窗体 - @Sunth - 博客园

【WPF】右下角弹出自定义通知样式(Notification)——简单教程 - catshitone的专栏 - CSDN博客

c#,使用WPF的Adorner实现iPhone上新邮件或消息提示效果—-实现(一) - CSDN博客

WPF实现Themes切换 - CSDN博客

对TabControl的简单优化 - 猴健居士 - 博客园

[Prism框架实用分享]如何在主程序中合理的弹出子窗体 - @Sunth - 博客园

Prism - 标签 - Jason Li - 博客园

Prism - Eric_K1m的专栏 - CSDN博客

WPF中DataGrid控件内Button的Command和CommandParameter的绑定 - jumtre的专栏 - CSDN博客

Prism.Interactivity 之 PopupWindowAction 用法简记 - 不老哥 - 博客园

WPF 漂亮的自定义分页控件 - 简书

免费的精品: Productivity Power Tools 动画演示 - stg609 - 博客园

c# 模拟表单提交,post form 上传文件、大数据内容 - CSDN博客

搭建基于MongoDB的文件管理系统(一) - 作业部落 Cmd Markdown 编辑阅读器

在ASP.NET MVC中实现大文件异步上传(2) - 51CTO.COM

黄聪:如何用代码设置控制自己网站的网页在360浏览器打开时强制优先使用极速模式,而非兼容模式 - 黄聪 - 博客园

分布式架构理论篇 - CSDN博客

.NET领域驱动设计系列 - 随笔分类 - Learning hard - 博客园

矩阵的坐标变换(转) - Danny Chen - 博客园

移动端热更新方案(iOS+Android) - 七夜i - 博客园

SQL Server 2012 开发新功能 序列对象(Sequence) - zhangyoushugz - 博客园

标签: CQRS | Edison Xu’s Blog

用JS获取地址栏参数的方法(超级简单) - 墟零 - 博客园

微信开发笔记——微信网页登录授权,获取用户信息 - =金刚= - 博客园

微信公众号开发之网页授权获取用户基本信息 - 潇十一郎 - 博客园

Senparc.Weixin SDK 微信公众号 .NET 开发教程 索引 - SZW - 博客园

asp.net core 引入vue工程 - 简书

C# Socket使用以及DotNetty和Supersocket 框架 - netlock - 博客园

读取社保卡信息 - dozeoo - 博客园

.net core的配置介绍(一):IConfiguration - 没有星星的夏季 - 博客园

.Net Core DevOps -免费用Azure四步实现自动化发布(CI/CD) - 布洛克菲勒 - 博客园

Robot Framework官方教程(一)入门 - 简书

[[C#].NET/C#程序开发中如何更优美地实现失败任务重试的逻辑? | 码友网](https://codedefault.com/s/what-is-the-cleanest-way-to-write-retry-logic-in-csharp-application#:~:text=在.NET%2FC%23的程序开发中,有时候需要对一些失败的任务进行多次的重试,如果重试的次数达到我们设定的阀值,则再放弃任务,比如有以下的C%23伪代码: int retries %3D 3 %3B while (true),0) throw %3B else Thread.Sleep (1000)%3B } })

Polly .NET瞬时故障处理 - 重试_水墨长天的博客-CSDN博客_polly 重试

从零搭建一个IdentityServer——集成Asp.net core Identity - 7m鱼 - 博客园

asp.net core使用identity+jwt保护你的webapi(二)——获取jwt token - xhznl - 博客园

ASP.NET Core 6.0 添加 JWT 认证和授权 - 芦荟柚子茶 - 博客园

【Vue】Vue与ASP.NET Core WebAPI的集成 :: Garfield-加菲的博客 — 专注于IT互联网,Web技术,.Net, .Net Core,Node.js, Go语言(golang)、前端框架、项目管理、软件架构 只有原创,没有转载,只有实践,才会成文。

.Net gRPC使用Jwt验证详细教程 - 冰河洗剑

.NET 6使用Redis - Lulus - 博客园

ASP.NET Core gRPC 使用 Consul 服务注册发现 - 晓晨Master - 博客园

ASP.Net Core下Authorization的几种方式 - 简书

C#常用的加密算法:MD5、Base64、SHA1、SHA256、HmacSHA256、DES、AES、RSA - TomLucas - 博客园

VS2022 安装.NET4.5目标包 - Stay627 - 博客园

搭建面向NET Framework的CI/CD持续集成环境(一)Windows服务器安装Jenkins - ElijahZeng - 博客园

搭建面向NET Framework的CI/CD持续集成环境(二)自动代码分支编译构建 - ElijahZeng - 博客园

C#中的时间戳 - 简书

OpenCV

OpenCV - 迭代的是人,递归的是神 - 博客频道 - CSDN.NET

OpenCV入门指南 - MoreWindows Blog - 博客频道 - CSDN.NET

[使用SeetaFace和Dlib实现人脸识别 | 第七区](https://zone-7.github.io/2018/02/24/others/2018-02-24-使用SeetaFace 和Dlib实现人脸识别/)

6.Qt实时人脸识别和Sqlite数据库_哔哩哔哩_bilibili

使用OpenCV+Dlib ,多线程实现人脸检测+人脸识别(C++ ) - 知乎

Dlib学习人脸比对_dlib 人脸比对-CSDN博客

dlib库包的介绍与使用,opencv+dlib检测人脸框、opencv+dlib进行人脸68关键点检测,opencv+dlib实现人脸识别,特征聚类 - 掘金

qt+opencv+dlib 人脸识别,可以存储人脸数据_dlib qt-CSDN博客

如何计算两个人脸的相似度 - 知乎

c++ dlib + opencv + facenat 进行人脸比对 - CSDN文库

机器学习库dlib的C++编译和使用(windows和linux) - 知乎

C++

Qt Creator 配置第三方库和头文件_qt creator加载第三方库csdn-CSDN博客

Qt中开启线程的五种方法_qt 开启线程-CSDN博客

Qt事件耦合器实现(类似C#的Prism中的事件耦合器)_qt偶合-CSDN博客

c/c++编译:使用CMAKE进行跨平台开发_cmake 跨平台_随便写写。的博客-CSDN博客

C++跨平台(二):grpc和zmq的方案预研_CodeBowl的博客-CSDN博客_grpc跨平台

arm平台Openssl交叉编译_aron566的博客-CSDN博客_arm openssl交叉编译

(11条消息) Qt 添加MSVC2017编译器(2022年保姆级教程,不安装完整VS)_Copperxcx的博客-CSDN博客_msvc2017

(13条消息) C 语言实现面向对象编程_onlyshi的博客-CSDN博客_c语言面向对象的编程方法

(14条消息) Hello Qt——QtCreator代码格式化_天山老妖的博客-CSDN博客_qt 格式化代码

(14条消息) qtcreator 格式化代码_北极熊的奋斗史的博客-CSDN博客

(14条消息) Ubuntu20.10编译安装Qt4.8.7_wanyiba的博客-CSDN博客

10.搭建vs2010+Qt4.8.5+QtCreator3.4.0开发环境_xhome516的博客-CSDN博客_qt4.8.5+vs2010环境配置

在 VS2015 中使用 Qt4 - 简书

魔改 Qt Creator 插件框架(附源码)

qt plugins 插件框架_qt 插件框架-CSDN博客

深入理解QtCreator的插件设计架构 - 简书

GoLang

后端 - Go 1.18 泛型全面讲解:一篇讲清泛型的全部_个人文章 - SegmentFault 思否

【Golang】关于Go中logrus的用法 - 踏雪无痕SS - 博客园

Go Grpc Jwt身份认证 - dz45693 - 博客园

数据库:PostgreSQL加载timescaledb拓展异常FATAL: extension “timescaledb” must be preloaded_hahahafree的博客-CSDN博客

spring cloud + kubeedge_鲜卑大帝的博客-CSDN博客

[centos网卡配置详解 - vinter_he - 博客园](https://www.cnblogs.com/vinter/p/12547698.html#:~:text=centos网卡配置文件一般位于:%2Fetc%2Fsysconfig%2Fnetwork-scripts%2F 文件名一般为:ifcfg-eno或者ifcfg-eth0类似的文件,可以先用ip,addr 命令或者是ifconfig命令查看网卡信息)

最全的Python虚拟环境使用方法 - 知乎

Ubuntu:PostgreSql安装PostGis、TimeScaleDB插件_love_QJP的博客-CSDN博客

(13条消息) golang pbkdf2加密存储用户密码_藏呆羊的博客-CSDN博客

Go 实现 PBKDF2 加密算法 - beihai blog

gin集成casbin - 掘金

手把手,带你从零封装Gin框架(四):数据库初始化(GORM) - 掘金

手把手,带你从零封装Gin框架(五):静态资源处理 & 优雅重启服务器 - 掘金

Consul 入门-gRPC 服务注册与发现 - 掘金

Docker部署Go程序 - 掘金

【Vscode】调试go语言程序的最佳实践 - 腾讯云开发者社区-腾讯云

GO操作influxdb_书笑生的博客-CSDN博客_go influxdb

golang 编程规范 - 项目目录结构 - 简书

基于 Gin 模块化开发 API 框架设计_水痕01的博客-CSDN博客_gin开发api

国标

spring boot +netty 解析国标协议(GB/T 26875.3-2011)用户信息传输装置_qq_41655468的博客-CSDN博客_国标26875协议

边缘计算

(11条消息) KubeEdge环境搭建-实现原理-官方计数器示例运行成功_Counter Demo_尖耳朵的阿凡达妹妹的博客-CSDN博客_kubeedge搭建

(11条消息) 学习k8s的接口(Api)使用,并试着调用KubeEdge中(计数器demo)设备的信息_在不甘与平凡中逆水行舟的博客-CSDN博客_k8s接口

从零开始——在Ubuntu22.04系统中部署KubeEdge架构_怂怂是张的博客-CSDN博客_ubantu部署kubeedge

kubernetes - 【KubeEdge】KubeEdge部署小指南-Edge节点接入(避坑)_个人文章 - SegmentFault 思否

k8s+kubeedge+sedna部署· - 鹦鹉理 - 博客园

Linux编译安装kubeedge_beyond阿亮的博客-CSDN博客_kubeedge编译安装

在 Kubernetes 中部署并使用 KubeEdge

源码安装kubeedge_rocsdu的博客-CSDN博客_cloudcore二进制

KubeEdge安装部署 - 请务必优秀 - 博客园

Linux

gentoo 安装docker 折腾手记 - 贵贵的博客 - 开发|架构|开源|共享

【图片】Archlinux安装小记(2016.3.3)【archlinux吧】_百度贴吧

【图片】arch/manjaro安装antergos全套主题(包括DE,DM,grub2)【archlinux吧】_百度贴吧

Arch Linux 安装、配置、美化和优化 - petercao - 博客园

Centos配置国内yum源-zhuzusong-ChinaUnix博客

CentOS yum 源的配置与使用 - David_Tang - 博客园

RHEL/CentOS/Fedora各种源(EPEL、Remi、RPMForge、RPMFusion)配置_Linux教程_Linux公社-Linux系统门户网站

不得不装的CentOS的三大yum源centos

ubuntu14.04中离线安装docker - 博客频道 - CSDN.NET

CentOS-6.5离线安装docker-1.7教程 - tuzhutuzhu的专栏 - 博客频道 - CSDN.NET

Android

android实现微信自动抢红包 - oden的博客 - 博客频道 - CSDN.NET

教你一步步实现Android微信自动抢红包_Android_脚本之家

Android AccessibilityService实现微信自动抢红包 - 博客频道 - CSDN.NET

Android微信抢红包外挂 源代码 - 翊轩LeOn - 博客频道 - CSDN.NET

AndroidCompile - VideoLAN Wiki

Android唤醒、解锁屏幕代码实例_Android_脚本之家

Android程序主动点亮&解锁屏幕的实现 - 移动平台应用软件开发技术 - 博客频道 - CSDN.NET

Android辅助功能AccessibilityService与抢红包辅助_Android_脚本之家

android抢红包代码解析支持微信与QQ - Android移动开发技术文章_手机开发 - 红黑联盟

Android蓝牙搜索设备,向其发送数据并接收-刘宇 - 刘宇的博客 - CSDN博客

android 蓝牙设备监听广播 - LeslieFang - 博客园

Android总结篇系列:Android广播机制 - Windstep - 博客园

Android 蓝牙开发(一)蓝牙通信 - 奋斗的菜鸟ing - CSDN博客

Android开发之蓝牙通信(一) - AnalyzeSystem的博客 - CSDN博客

Android Bluetooth(蓝牙)实例 - Android开发教程™

android 蓝牙通信编程 - Gabriel的专栏 - CSDN博客

Android 实现蓝牙客户端与服务器端通信 - Android移动开发技术文章_手机开发 - 红黑联盟

详解Android——蓝牙技术 带你实现终端间数据传输_Android_脚本之家

android自带的示例程序 BluetoothChat 变蓝牙串口助手 - Android小子的博客笔记 - CSDN博客

Android 蓝牙开发基本流程 - q610098308的专栏 - CSDN博客

Android蓝牙串口通信模板及demo,trick - MetalSeed - CSDN博客

Android Ble 4.0 蓝牙开发交互 - Lucy__的博客 - CSDN博客

Android和BLE模块连接通信 - Spades-S - 博客园

Android BLE开发——Android手机与BLE终端通信初识 - CKTim - 博客园

Android 蓝牙4.0(BLE)开发实现对蓝牙的写入数据和读取数据 - It_BeeCoder的博客 - CSDN博客

Android游戏源码大合集(主要是AndEngine和Libgdx的) - 下载频道 - CSDN.NET

Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果 - 下载频道 - CSDN.NET

Android滑动菜单特效实现,仿人人客户端侧滑效果demo - 下载频道 - CSDN.NET

Android教你如何一分钟实现下拉刷新功能demo - 下载频道 - CSDN.NET

Android第三方开源框架ImageLoader的完美Demo - 下载频道 - CSDN.NET

Android高手进阶之自定义View,自定义属性(带进度的圆形进度条) - 下载频道 - CSDN.NET

Android实现ListView的A-Z字母排序和过滤搜索功能 - 下载频道 - CSDN.NET

Android动画效果集合开源APP(BaseAnimation1.3) - 下载频道 - CSDN.NET

Android 通知栏Notification的全面整合学习 - 下载频道 - CSDN.NET

Android 打造史上最简单的侧滑菜单 - 下载频道 - CSDN.NET

164个完整的Java源程序代码 - 下载频道 - CSDN.NET

vlc-android源码编译过程记录 - 精神邋遢的民工 - 博客园

vlc-android 中调用用libvlcjni.so实现流媒体播放 - memegood123的专栏 - 博客频道 - CSDN.NET

vlc-android 获取MediaPlayerEncounteredError,MediaPlayerBuffering等各种事件的响应 - memegood123的专栏 - 博客频道 - CSDN.NET

【VLC-Android】vlc-android简例 - 农民伯伯 - 博客园

ffmpeg2.2在ubuntu下使用NDK编译——并在android工程下测试使用 - wainiwann - 博客园

Android版本-编译VLC - 雨の殇的天空 - 博客频道 - CSDN.NET

vlc-android源码的编译 - Find A Way 的博客 - 博客频道 - CSDN.NET

Ubuntu12.04编译vlc-android详细流程 - wainiwann - 博客园

Linux 下编译Android-VLC开源播放器详解(附源码下载) - 泡泡糖 - 博客园

LIBTOOL is undefined 问题的解决方法 - Sky_qing的专栏 - 博客频道 - CSDN.NET

基于Darwin实现的分布式流媒体直播服务器系统 - Babosa的专栏 - 博客频道 - CSDN.NET

DyncLang/DevLiveBook: 励志成为较全的直播技术导航_AnyRTC

Android wifi无线调试App新玩法ADB WIFI - 简书

WebRTC

使用WebRTC搭建前端视频聊天室——信令篇 - 说学逗唱 - SegmentFault

[部署rfc5766-turn-server--谷歌推荐的开源穿透服务器 复制链接] - 康林工作室 - 博客频道 - CSDN.NET

编译rfc5766-turn-server搭建turn服务器-码农场

Ubuntu下安装TURN Server (rfc5766-turn-server) - 不急不徐,持之以恒。 - BlogJava

随笔列表第7页 - RTC.Blacker - 博客园

C#+WebSocket+WebRTC多人语音视频系统 - 甩葱哥丶的个人空间 - 开源中国社区

一步一步搭建客服系统 (1) 3分钟实现网页版多人文本、视频聊天室 (含完整源码) - 疯吻IT - 博客园

web即时通信1–WebSocket与WebRTC的三种实现方式对比 —核心网络

在 Asp.NET MVC 中使用 SignalR 实现推送功能 - 罗朝辉(飘飘白云) - 博客频道 - CSDN.NET

java 调用wsdl的方式 - SamWu - 博客园

K8s

暴露redis-cluster到k8s集群外部.md_ll577644332的博客-CSDN博客

[(14条消息) kubernetes]-k8s部署单节点redis_爷来辣的博客-CSDN博客_k8s部署单节点redis

Docker - 实现本地镜像的导出、导入(export、import、save、load)

Microk8s 安装 与使用指南 - 腾讯云开发者社区-腾讯云

基于WSL2和Kind或Minikube:搭建Windows版Kubernetes_Kubernetes中文社区

kubeadm搭建k8s集群 - 请务必优秀 - 博客园

傻瓜式教学: Debian安装k3s(长期维护版本) | Solitudes

在 Kubernetes 上部署 Drone 持续集成环境 | Hanggi - NGNL

gitea+drone+kubernetes搭建devops平台_gitea+drone+k8s-CSDN博客

k3s containerd 配置 mirror 和 insecure - 知乎

将 minikube 的服务暴露到宿主机外 · jtr109’s Castle

嵌入式

〖嵌入式〗_叶帆的博客-CSDN博客

[耗时一周总结的嵌入式学习路线,超详细 - 知乎](https://zhuanlan.zhihu.com/p/531416610#:~:text=全文整体的学习路线: 嵌入式基础学习 -> 51单片机 -> STM32单片机 -> RTOS篇,ARM%2BLinux 每一个部分,也都从 学习内容 , 学习建议 , 学习资料 三个方面来展开,层层深入,步步指引。)

[熬夜肝了一份 C++/Linux 开发学习路线 - 帅地 - 博客园](https://www.cnblogs.com/kubidemanong/p/15151762.html#:~:text=熬夜肝了一份 C%2B%2B%2FLinux 开发学习路线 1 一、C%2B%2B 基础 (3-6个月) 2,6 六、数据结构与算法 (3-6%2B月) 7 七、项目 (2个月左右) 8 八、学习顺序)

Linux C/C++ 服务器/后端开发/后台开发学习路线_Linux服务器开发的博客-CSDN博客

VSCode配置C/C++环境 - 知乎

linux 下C语言学习路线 - lipps - 博客园

linux 下C语言学习路线_老徐拉灯的博客-CSDN博客_如何在linux中学习c语言

Linux系统开发学习路线_llhh33的博客-CSDN博客

go-logrus 日志框架封装使用 - 简书

微信小程序连接MQTT服务器全过程_白白的昕的博客-CSDN博客

这些操作删除console.log的方法,你都知道吗 - 知乎

Go语言相关书籍推荐(从入门到放弃) - 知乎

Gitea 与 Drone 集成实践:完全基于 Docker 搭建的轻量级 CI/CD 系统 - Gitea - 博客园

Gitea+Drone+Rancher CI/CD持续集成解决方案 - 掘金

有哪些值得推荐的c/c++开源框架与库 - 知乎

C语言网络编程(1)— UDP通信(这篇写得很详细,也讲了怎么借助网络调试助手)_c语言udp通信_TYINY的博客-CSDN博客

用C写一个UDP发送和接收程序_udp发送 c_11061104的博客-CSDN博客

Qt单个实例运行 - SingleApplication_yizhou2010的博客-CSDN博客

Qt应用程序的单例化(程序只运行一个实例)_qt中文件锁单例与共享内存单例_兜黎的博客-CSDN博客

C#获取硬盘序列号_lilin8905的博客-CSDN博客

消息摘要算法MD5图解及C语言实现 - 知乎

sha256 C语言实现_qq_43176116的博客-CSDN博客

HMAC-SHA256签名加密 C语言实现+例子_hmacsha256在线加密_Clnulijiayou的博客-CSDN博客

memcpy 合并数组 拷贝多个数组 memcpy采坑记_斗转星移3的博客-CSDN博客

C语言实现MD5加密,竟如此简单! - 腾讯云开发者社区-腾讯云

Golang领域模型-CQRS - Go语言中文网 - Golang中文社区

Go进阶10:logrus日志使用教程 | Go&Rust🦀

Go解决TCP粘包_go tcp粘包_向阳的野草的博客-CSDN博客

Go网络编程之UDP通信和文件传输 - 简书

kubeedge+edgemesh安装笔记_抓不到老鼠的汤姆的博客-CSDN博客

Golang最强大的访问控制框架casbin全解析 - 轩脉刃 - 博客园

ASP.NET MVC 5实现基于Quartz.net 的任务调度管理平台(一)_风神修罗使的博客-CSDN博客

Asp.Net Core2.0 基于QuartzNet任务管理系统 - ice.ko - 博客园

STM32常见通信方式(TTL、RS232、RS485、I2C,SPI,CAN)总结_有人用过bs32f103_位文杰TOP的博客-CSDN博客

Go语言实现websocket服务器 - 知乎

C++中的queue类、QT中的QQueue类 - 诺谦 - 博客园

05-部署持续部署服务-Drone(上) - 知乎

[Azure Devops] 使用 Azure Pipelines 实现 CI - 腾讯云开发者社区-腾讯云

使用Gitea+Drone来搭建自己的轻量级CI/CD自动构建平台 - 饭饭’s Blog

【小白向】基于Docker使用Gogs,Drone以及drone-runner-docker的自动化部署 - JohnBeBee - 博客园

基于etcd的服务发现与注册 - 掘金

C#实现ModBusRtu协议 - 知乎

MODBUS RTU MASTER的C语言代码 - r_jw - 博客园

FreeMobus移植在FreeRTOS上的移植(从机) - 云乐 - 博客园

grpc 双向通道 编写聊天室_grpc 双通道_tianv5的博客-CSDN博客

队列的实现(C语言)_vitobo的博客-CSDN博客

FreeRTOS(教程非常详细)_不秃也很强的博客-CSDN博客

c语言建立队列(顺序队列、循化队列和链式队列)_循环队列(数组)和链队列的建立及基本算法_落春只在无意间的博客-CSDN博客

数据结构STL——golang实现队列queue - 知乎

[eShopOnContainers 知多少1]:总体概览 - 「圣杰」 - 博客园

实现自己的.NET Core配置Provider之Yaml - BobTian - 博客园

Redis 学习笔记(六)Redis 如何实现消息队列 - 归斯君 - 博客园

【opencv实战】海康摄像rtsp流不同方案下的时延测试(Ing)_rtsp怎么测量拉流延时_昌山小屋的博客-CSDN博客

5分钟教你搭建Unity云渲染服务 - 知乎

ABP EF Core多数据库支持_abp多数据库支持_娃都会打酱油了的博客-CSDN博客

Hadoop教程 - yuan_xw的专栏 - 博客频道 - CSDN.NET

如何给变量取个简短且无歧义的名字_知识库_博客园

AutoMapper小结 - 呆河马 - 博客园

AutoMapper完成Dto与Model的转换 - kuangkro - 博客园

EF中Repository模式应用场景 - 指尖流淌 - 博客园

WPF 引用DLL纯图像资源包类库中的图片 - 耿爱学 - 博客园

WPF 如何将软件的所有图片保存在DLL内供主程序调用 - 蓝山咖啡的博客 - 博客频道 - CSDN.NET

三分钟教你学Git(十四) 之 线下传输仓库 - hongchangfirst - 博客频道 - CSDN.NET

使用epublib自动生成epub文件 - zhyoulun的专栏 - 博客频道 - CSDN.NET

如何用.net c# 读取epub格式文件 - szliszt的专栏 - 博客频道 - CSDN.NET

CMake 良心教程,教你从入门到入魂 - 知乎

(14条消息) 解决报错Cannot find module ‘webpack-cli/bin/config-yargs’_sxs7970的博客-CSDN博客_yargs 卸载

详解Docker 容器基础系统镜像打包_docker_脚本之家

一种docker基础镜像制作方法 - 老胡的笔记 - 博客频道 - CSDN.NET

Npoi操作excel - 张龙豪 - 博客园

非常详细的 Docker 学习笔记 - OPEN 开发经验库

CentOS-6.5离线安装docker-1.7教程 - tuzhutuzhu的专栏 - 博客频道 - CSDN.NET

用JIRA管理你的项目——(一)JIRA环境搭建 - Snowolf的意境空间! - ITeye技术网站

[JIRA] 最新Linux安装版本jira6.3.6安装破解以及数据导入的详细步骤-mchdba-ITPUB博客

Gradle Android最新自动化编译脚本教程(提供demo源码) - 心有灵犀鬼才心 - 博客频道 - CSDN.NET

Ubuntu20.04上安装Android Studio_ubuntu20.04安装android studio-CSDN博客

超全的auto.js基础操作,目前是autoX.js的控制方式。2023年9月23日更新!(第3/4章) - 知乎

[无障碍服务AccessibilityService详解-CSDN博客](https://blog.csdn.net/weixin_42574892/article/details/120042709#:~:text=常见无障碍服务示例 1 开关访问:允许行动不便的 Android 用户使用一个或多个开关与设备进行交互。,2 语音访问:允许行动不便的 Android 用户使用语音命令控制设备。 3 Talkback:视力受损或盲人用户常用的屏幕阅读器。)

Android 定时任务的8种实现方法 - 天涯海角路 - 博客园

通过C#将PDF快速导出为图片 - 个人文章 - SegmentFault 思否

[使用 C# 将 PDF 转换为图像](https://blog.conholdate.com/zh/total/convert-pdf-to-images-using-csharp/#:~:text=使用 C%23 将 PDF 转换为 PNG 图像 1,使用页码和输出 PNG 图片路径调用 Process (Page%2C String) 方法,将页面转换为 PNG。)

基于gitea+K3s实现DevOps/CI/CD - 掘金

InfluxDB安装以及使用 - 知乎

[c++ convert pdf to image-掘金](https://juejin.cn/s/c%2B%2B convert pdf to image)

设计一个通用的【系统操作日志】模块 - 知乎

超简单实现vr看房子、360全景图(附完整demo) - 掘金

微信小程序音视频与WebRTC互通的技术思路和实践 | 微信开放社区

实践修复 Linux 无法启动 EFI 故障 - 知乎

WPF:将调试信息输出到控制台-CSDN博客

使用 Harbor 作为 K3S 的镜像代理缓存后端 - roy2220 - 博客园

ubuntu22.04安装kubernetes1.26 (使用containerd)_ubuntu安装containerd-CSDN博客

用 Helm 在 k8s 上快速搭建 MySQL 主从集群 ,并提供对外访问 - 掘金

helm部署mysql-腾讯云开发者社区-腾讯云

ubuntu 20.4安装k8s 1.24.0、1.28.0(使用containerd)_ubuntu containerd 安装-CSDN博客

如何在 Ubuntu 22.04 上安装 Containerd 容器运行时

C# 断言 Assert - Mzhangyl - 博客园

C语言和设计模式(总结篇) 用了多年的C_c程序设计 模式-CSDN博客

Redis 集群模式与哨兵模式:详细对比与实例解析_redis cluster和哨兵区别-CSDN博客

Redis6搭建高可用的多主多从集群-阿里云开发者社区

redis集群搭建(非常详细,适合新手)_redis哨兵模式两主多从-腾讯云开发者社区-腾讯云

k8s安装redis主从版-腾讯云开发者社区-腾讯云

k8s中部署redis集群(三主三从) - 云起时。 - 博客园

【授人以渔】从根源上解决NAS影视库(Jellyfin/Emby/PLEX)刮削问题,群晖威联通华硕通用教程_NAS存储_什么值得买

etcd实现服务发现 - 烟花易冷人憔悴 - 博客园

Qt编译器迁移:从MinGW到MSVC - MrBeanC-Blog

Centos/ubuntu-搭建简单的国内代理服务器(socks5/http代理)-腾讯云开发者社区-腾讯云

2022 最新 Mac Vim 开发环境的部署与配置_macvim-CSDN博客

Gentoo网络设置 - 简书

arping工具使用_arping使用方法-CSDN博客

ping、arp、tracert三大命令详细用法,弄懂立马成大神 - 知乎

C语言函数大全及详解 - 知乎

移植Qt到ARM平台及搭建Qt交叉编译环境_qt arm-CSDN博客

嵌入式Linux开发: 从0开始编译并启动ARM Linux内核(全志)_arm linux虚机镜像怎么启动-CSDN博客

Linux系统7个运行级别(runlevel)详解 - 知乎

Linux Ubuntu 20.04 —添加开机启动(服务/脚本) - Areas - 博客园

WebApi+Grpc+Jwt身份认证 - zeran - 博客园

.NetCore使用Grpc通信,简单的微服务+JWT认证 - zeran - 博客园

网络代理神器ProxyChains快速安装配置使用 - CentOS_centos 安装proxychains-CSDN博客

amd64/UEFI/systemd/gnome/gentoo安装过程记录_gentoo安装gnome_Freeman Z的博客-CSDN博客

Gentoo安装流程分享(step by step),第一篇之基本系统的安装 - 知乎

FreeBSD Jail 使用 ipfw 进行 NAT 与端口转发 | VGOT Blog

C++后端开发路线——愿我踩过的坑,你可以绕着走-电子工程专辑

【精选】Asp.net core 身份认证框架 Microsoft Identity的使用以及如何使用Idengtity创建自带的用户模型SignInManager和UserManager的使用等等_microsoft.aspnetcore.identity_爱吃奶酪的松鼠丶的博客-CSDN博客

git 瘦身 解决 .git文件夹过大的问题_git瘦身-CSDN博客

.Net Core JWT 动态设置接口与权限,.Net Core官方的 JWT 授权验证-腾讯云开发者社区-腾讯云

asp.net core使用identity+jwt保护你的webapi(一)——identity基础配置 - xhznl - 博客园

.NET Core中JWT+OAuth2.0实现SSO,附完整源码(.NET6) - 包子wxl - 博客园

.NET 6 集成 IdentityServer4+AspNetCore Identity 读取本地数据表用户

[编程变量命名规则及单词缩写字典 - 五维思考 - 博客园](https://www.cnblogs.com/zhaoshujie/p/14983712.html#:~:text=编程单词缩写规则 1 使用变量名中每个有典型意义的单词。 如 Count of Failure 写成,ed 等。 如 Paging Request 写成 PagReq 。)

CTK完整教程(OSGI for C++ 实现 C++ Qt 模块化)-腾讯云开发者社区-腾讯云

CTK完整教程(OSGI for C++ 实现 C++ Qt 模块化) | 来唧唧歪歪(Ljjyy.com) - 多读书多实践,勤思考善领悟

编译 CTK(Win10 + Qt 5.14.0 + VS2015)_ctk编译-CSDN博客

将线程绑定在某个具体的CPU逻辑内核上运行_c# setthreadaffinitymask_皓月如我的博客-CSDN博客

Kubernetes 网络插件 Calico 完全运维指南 - 知乎

mysql5.7性能提升一百倍调优宝典_mysql5.7调优-CSDN博客

mysql5.7配置文件优化 - John-Python - 博客园

MySQL5.7配置my.ini文件_mysql5.7配置文件my.ini_It&code的博客-CSDN博客

mysql5.7配置文件详解 - 知乎

装X神器,让你的grafana看板变得炫酷起来-腾讯云开发者社区-腾讯云

Go与SOAP - Go语言中文网 - Golang中文社区

[C#]Soap服务通讯(客户端)_c# soap_eahao的博客-CSDN博客

Onvif协议:使用gSOAP创建SOAP调用实例_gsoap 生成soap_serve-CSDN博客

postgresql + pgpool 构建容灾高可用集群(数据同步流复制/主备自动切换) - 世间草木 - 博客园

K8S Calico网络插件 - RidingWind - 博客园

(三)InfluxDB入门(借助Web UI)_influxdb web界面_灵泽~的博客-CSDN博客

Docker 私有仓库搭建 - 程序员果果 - 博客园

Docker容器:本地私有仓库、harbor私有仓库部署与管理_Zhang110_的博客-CSDN博客

Docker 私有镜像仓库的搭建及认证 - 知乎

Dapper的基本使用,Insert、Update、Select、Delete-CSDN博客

QT学习笔记-QT安装postgresql驱动_postgresql 驱动_CodingPioneer的博客-CSDN博客

Qt 编译MySQL数据库驱动——MSVC版本_qt mysql 编译_英雄施工的博客-CSDN博客

[Kapacitor在Chronograf中的配置和使用 - 墨天轮](https://www.modb.pro/db/107297#:~:text=添加一个Kapacitor实例 1 在左侧导航栏中,单击配置齿轮图标。 已加载InfluxDB源列表。 2 在列表中的” Acitve Kapacitor”标题下的最右列中,找到InfluxDB源,单击”,4 单击连接按钮。 如果”连接详细信息”正确,则会显示一条成功消息,并且新部分将显示”配置警报端点”。 5 如果使用了第三方警报服务或SMTP,请更新”配置警报端点”部分中的第三方设置。 6 点击Send TestAlert按钮,会发送一条测试邮件到邮箱)

单机版Ceph环境部署,Linux平台 - 知乎

基于gin框架和jwt-go中间件实现小程序用户登陆和token验证 - zzayne - 博客园

OpenIddict 登录及详细流程解析 - 龙码精神 - 博客园

Linq动态拼接Expression表达式(可多表字段判别)_c# linq 动态分组 expression-CSDN博客

【Qt开源项目推荐】目录(持续更新)_maoboxxx的博客-CSDN博客

Github上的一些高分Qt开源项目【多图】_qt项目_雪域迷影的博客-CSDN博客

Prometheus+Grafana - 随笔分类 - 曹伟雄 - 博客园

在Linux中修改打开文件数量限制的3种方法 - 知乎

mysql中grant all privileges on赋给用户远程权限_李在奋斗的博客-CSDN博客

使用 etcd 作为服务配置中心 | Ray’s blog

.Net Core微服务系列–配置中心 - RstarYan - 博客园

使用locust进行Websocket压力测试和接口压力测试-腾讯云开发者社区-腾讯云

Loki & Promtail 详解-腾讯云开发者社区-腾讯云

(1条消息) 示例:WPF中使用Grid+Margin实现抽屉菜单效果_wpf 抽屉式菜单_He BianGu的博客-CSDN博客

最新超详细C++经典Boost库介绍_Come_code的博客-CSDN博客

Unity开源项目整理(长期整理+不定期更新)_CloudHu1989的博客-CSDN博客

ubuntu14.04中离线安装docker - 博客频道 - CSDN.NET

Docker-容器的操作 - 头痛不头痛 - 博客园

.NET Core 跨平台发布(dotnet publish) - LineZero - 博客园

常用docker命令,及一些坑 - edwardsbean的专栏 - 博客频道 - CSDN.NET

Docker常用命令 - 小爷,有点狂 - 博客频道 - CSDN.NET

Docker 常用命令 - 碉堡了 - 博客园

搭建开发环境 - React Native 中文网

百度地图内网开发项目 - javascript_net的专栏 - 博客频道 - CSDN.NET

自己动手写一个前端路由插件 - 最骚的就是你 - 博客园

Autofac 组件、服务、自动装配 《第二篇》 - 逆心 - 博客园

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案 - 萌萌的It人 www.itmmd.com - CSDN博客

js数据验证集合、js email验证、js url验证、js长度验证、js数字验证等简单封装_表单特效_脚本之家

搭建自己的私有云服务器私有NAS系列 | 入坑大数据

CentOS6.5安装MySQL5.7详细教程 - $nail - 博客园

mysql 5.7配置项最详细的解释 - CSDN博客

Prism - 随笔分类 - 西夏 - 博客园

一步步实现 Prism + MEF(一)— 搭建框架 - 无主之城 - 博客园

WPF: WPF 中的 Triggers 和 VisualStateManager - WPInfo - 博客园

C#多线程和线程池 - 夜、微凉 - 博客园

C# List源码分析(一) - CSDN博客

c# List实现原理 - micDavid - 博客园

(粗译) Prism and WPF - 定制 Tab region adapter - 01部分 - tan_Cool - 博客园

WPF教程(十一)WPF中的命令行参数 - CSDN博客

(粗译) Prism and WPF - 定制 Tab region adapter - 02部分 - tan_Cool - 博客园

一个过滤Textbox输入的WPF Behavior - Alex Geng - 博客园

如何给 CI CD 服务器搭建上 .NET 5 构建和运行环境 - 云+社区 - 腾讯云

.net4.5部署到docker容器 - 张占岭 - 博客园

DevOps - Concourse - Anliven - 博客园

manjaro20安装后的配置、常用软件的安装、环境搭建 - Lanomw - 博客园

.net IOC框架 Unity&Autofac - 简书

解决 git 中文路径显示 unicode 代码的问题_u014028063的博客-CSDN博客_git 中文路径

(18条消息) 开发自己的gentoo LiveCD_gentoo 制作livecd_suirosu的博客-CSDN博客

开始使用gentoo linux——gentoo安装笔记(下) - devilyouwei - 博客园

(18条消息) 如何在UEFI系统上双引导 Arch Linux 和 Windows 10 ?_efi双引导切换_鸠摩智首席音效师的博客-CSDN博客

2021 Archlinux双系统安装教程(超详细) - 知乎

使用MQTTnet部署MQTT服务(转) - 吞硬币的小猪 - 博客园

一种基于RSA+AES算法实现的软件授权License设计思路(附源码) - 掘金

LSI 9217-8i 阵列卡 介绍以及卡刷 IT/IR 模式 - 简书

[Kubeedge部署指南 - KubeEdge使用文档 - 《openEuler 21.09 使用指南》 - 书栈网 · BookStack](https://www.bookstack.cn/read/openeuler-21.09-zh/KubeEdge-KubeEdge使用文档.md#4. 部署边缘端应用)

MVVM处理TreeView的SelectedItem的绑定的两种方式_treeviewitem绑定事件_lishuangquan1987的博客-CSDN博客

RK3399平台入门到精通系列讲解 - 总目录_rk3399学习_Linux 笔记的博客-CSDN博客

国标GB28181平台对接视频流 - 王纲 - 博客园

解放双手! bat 批处理自动化运行程序 - 知乎

Windows 批处理(bat)语法大全 - 赵青青 - 博客园

.net core gRPC与IdentityServer4集成认证授权 - 福禄渣渣辉 - 博客园

ESC/POS 控制指令 - ma_fighting - 博客园

小票打印ESC/POS命令集 - 简书

EPSON ESC/POS指令 - 简书

在NVMe硬盘安装ArchLinux+Windows11 | UrsusFeline-Blog

数字孪生系统开发讲解和源代码_Bigemap的博客-CSDN博客

[pgpool-ii4.1.2 高可用集群主备切换]配置部分 - 世间草木 - 博客园

JAVA-WVP+ZLMediaKit+MediaServerUI实现摄像头 GB28181推流播放录制-CSDN博客

几十款 WPF 控件 - UI 库,总有一款适合你 - 独立观察员

Virtualize Your Network on FreeBSD with VNET | Klara Inc (klarasystems.com)

Abstract Factory,抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类。 应用场景:一系列相互依赖的对象有不同的具体实现。提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合。

http://www.cnblogs.com/PatrickLiu/p/7596897.html

http://www.cnblogs.com/zhili/p/AbstractFactory.html

8种主要排序算法的C#实现

Excerpt

8种主要排序算法的实现及优化,包含选择排序,冒泡排序,插入排序,快速排序,归并排序,堆排序,希尔排序,基数排序。文末实际测试并比较。


新的一年到了,很多园友都辞职要去追求更好的工作环境,我也是其中一个,呵呵!

最近闲暇的时候我开始重温一些常用的算法。老早就买了《算法导论》,一直都没啃下去。

这本书确实很好,只是太难读了,总是读了几章就又读不下去了!工作上也几乎用不到。

我这段时间发现看这些排序算法比以前容易了很多,就借此机会将它们整理总结起来。

一是方便以后重温,二是可以应对笔试面试。同时也希望这篇博文可以帮助各位刚辞职和正在学习排序算法的园友。

PS:有可能实现的代码并不是最优的,如果有什么错误或者值得改进的地方,还请大家帮忙指出。

简介

排序算法是我们编程中遇到的最多的算法。目前主流的算法有8种。

  平均时间复杂度从高到低依次是:

     冒泡排序(o(n2)),选择排序(o(n2)),插入排序(o(n2)),堆排序(o(nlogn)),

     归并排序(o(nlogn)),快速排序(o(nlogn)), 希尔排序(o(n1.25)),基数排序(o(n))

这些平均时间复杂度是参照维基百科排序算法罗列的。

是计算的理论平均值,并不意味着你的代码实现能达到这样的程度。

例如希尔排序,时间复杂度是由选择的步长决定的。基数排序时间复杂度最小,

但我实现的基数排序的速度并不是最快的,后面的结果测试图可以看到。

本文代码实现使用的数据源类型为IList,这样可以兼容int[]和List(虽然int[]有ToList(),

List有ToArray(),哈哈!)。

选择排序

选择排序是我觉得最简单暴力的排序方式了。

以前刚接触排序算法的时候,感觉算法太多搞不清,唯独记得选择排序的做法及实现。

原理:找出参与排序的数组最大值,放到末尾(或找到最小值放到开头) 维基入口

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> SelectSort(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; data.Count - <span>1</span>; i++<span>)
</span><span> 4</span> <span> {
</span><span> 5</span> <span>int</span> min =<span> i;
</span><span> 6</span> <span>int</span> temp =<span> data[i];
</span><span> 7</span> <span>for</span> (<span>int</span> j = i + <span>1</span>; j &lt; data.Count; j++<span>)
</span><span> 8</span> <span> {
</span><span> 9</span> <span>if</span> (data[j] &lt;<span> temp)
</span><span>10</span> <span> {
</span><span>11</span> min =<span> j;
</span><span>12</span> temp =<span> data[j];
</span><span>13</span> <span> }
</span><span>14</span> <span> }
</span><span>15</span> <span>if</span> (min !=<span> i)
</span><span>16</span> <span> Swap(data, min, i);
</span><span>17</span> <span> }
</span><span>18</span> }

过程解析:将剩余数组的最小数交换到开头。

冒泡排序

冒泡排序是笔试面试经常考的内容,虽然它是这些算法里排序速度最慢的(汗),后面有测试为证。

原理:从头开始,每一个元素和它的下一个元素比较,如果它大,就将它与比较的元素交换,否则不动。

这意味着,大的元素总是在向后慢慢移动直到遇到比它更大的元素。所以每一轮交换完成都能将最大值

冒到最后。  维基入口

实现如下:

1
2
3
4
5
6
7
8
9
10
11
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> BubbleSort(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>for</span> (<span>int</span> i = data.Count - <span>1</span>; i &gt; <span>0</span>; i--<span>)
</span><span> 4</span> <span> {
</span><span> 5</span> <span>for</span> (<span>int</span> j = <span>0</span>; j &lt; i; j++<span>)
</span><span> 6</span> <span> {
</span><span> 7</span> <span>if</span> (data[j] &gt; data[j + <span>1</span><span>])
</span><span> 8</span> Swap(data, j, j + <span>1</span><span>);
</span><span> 9</span> <span> }
</span><span>10</span> <span> }
</span><span>11</span> }

过程解析:中需要注意的是j<i,每轮冒完泡必然会将最大值排到数组末尾,所以需要排序的数应该是在减少的。

很多网上版本每轮冒完泡后依然还是将所有的数进行第二轮冒泡即j<data.Count-1,这样会增加比较次数。

通过标识提升冒泡排序

在维基上看到,可以通过添加标识来分辨剩余的数是否已经有序来减少比较次数。感觉很有意思,可以试试。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> BubbleSortImprovedWithFlag(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>bool</span><span> flag;
</span><span> 4</span> <span>for</span> (<span>int</span> i = data.Count - <span>1</span>; i &gt; <span>0</span>; i--<span>)
</span><span> 5</span> <span> {
</span><span> 6</span> flag = <span>true</span><span>;
</span><span> 7</span> <span>for</span> (<span>int</span> j = <span>0</span>; j &lt; i; j++<span>)
</span><span> 8</span> <span> {
</span><span> 9</span> <span>if</span> (data[j] &gt; data[j + <span>1</span><span>])
</span><span>10</span> <span> {
</span><span>11</span> Swap(data, j, j + <span>1</span><span>);
</span><span>12</span> flag = <span>false</span><span>;
</span><span>13</span> <span> }
</span><span>14</span> <span> }
</span><span>15</span> <span>if</span> (flag) <span>break</span><span>;
</span><span>16</span> <span> }
</span><span>17</span> }

过程解析:发现某轮冒泡没有任何数进行交换(即已经有序),就跳出排序。

我起初也以为这个方法是应该有不错效果的,可是实际测试结果并不如想的那样。和未优化耗费时间一样(对于随机数列)。

由果推因,那么应该是冒泡排序对于随机数列,当剩余数列有序的时候,也没几个数要排列了!?

不过如果已经是有序数列或者部分有序的话,这个冒泡方法将会提升很大速度。

鸡尾酒排序(来回排序)

对冒泡排序进行更大的优化

冒泡排序只是单向冒泡,而鸡尾酒来回反复双向冒泡。

原理:自左向右将大数冒到末尾,然后将剩余数列再自右向左将小数冒到开头,如此循环往复。维基入口

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> BubbleCocktailSort(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>bool</span><span> flag;
</span><span> 4</span> <span>int</span> m = <span>0</span>, n = <span>0</span><span>;
</span><span> 5</span> <span>for</span> (<span>int</span> i = data.Count - <span>1</span>; i &gt; <span>0</span>; i--<span>)
</span><span> 6</span> <span> {
</span><span> 7</span> flag = <span>true</span><span>;
</span><span> 8</span> <span>if</span> (i % <span>2</span> == <span>0</span><span>)
</span><span> 9</span> <span> {
</span><span>10</span> <span>for</span> (<span>int</span> j = n; j &lt; data.Count - <span>1</span> - m; j++<span>)
</span><span>11</span> <span> {
</span><span>12</span> <span>if</span> (data[j] &gt; data[j + <span>1</span><span>])
</span><span>13</span> <span> {
</span><span>14</span> Swap(data, j, j + <span>1</span><span>);
</span><span>15</span> flag = <span>false</span><span>;
</span><span>16</span> <span> }
</span><span>17</span> <span> }
</span><span>18</span> <span>if</span> (flag) <span>break</span><span>;
</span><span>19</span> m++<span>;
</span><span>20</span> <span> }
</span><span>21</span> <span>else</span>
<span>22</span> <span> {
</span><span>23</span> <span>for</span> (<span>int</span> k = data.Count - <span>1</span> - m; k &gt; n; k--<span>)
</span><span>24</span> <span> {
</span><span>25</span> <span>if</span> (data[k] &lt; data[k - <span>1</span><span>])
</span><span>26</span> <span> {
</span><span>27</span> Swap(data, k, k - <span>1</span><span>);
</span><span>28</span> flag = <span>false</span><span>;
</span><span>29</span> <span> }
</span><span>30</span> <span> }
</span><span>31</span> <span>if</span> (flag) <span>break</span><span>;
</span><span>32</span> n++<span>;
</span><span>33</span> <span> }
</span><span>34</span> <span> }
</span><span>35</span> }

过程解析:分析第i轮冒泡,i是偶数则将剩余数列最大值向右冒泡至末尾,是奇数则将剩余数列最小值

向左冒泡至开头。对于剩余数列,n为始,data.Count-1-m为末。

来回冒泡比单向冒泡:对于随机数列,更容易得到有序的剩余数列。因此这里使用标识将会提升的更加明显。

插入排序

插入排序是一种对于有序数列高效的排序。非常聪明的排序。只是对于随机数列,效率一般,交换的频率高。

原理:通过构建有序数列,将未排序的数从后向前比较,找到合适位置并插入。维基入口

第一个数当作有序数列。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> InsertSort(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>int</span><span> temp;
</span><span> 4</span> <span>for</span> (<span>int</span> i = <span>1</span>; i &lt; data.Count; i++<span>)
</span><span> 5</span> <span> {
</span><span> 6</span> temp =<span> data[i];
</span><span> 7</span> <span>for</span> (<span>int</span> j = i - <span>1</span>; j &gt;= <span>0</span>; j--<span>)
</span><span> 8</span> <span> {
</span><span> 9</span> <span>if</span> (data[j] &gt;<span> temp)
</span><span>10</span> <span> {
</span><span>11</span> data[j + <span>1</span>] =<span> data[j];
</span><span>12</span> <span>if</span> (j == <span>0</span><span>)
</span><span>13</span> <span> {
</span><span>14</span> data[<span>0</span>] =<span> temp;
</span><span>15</span> <span>break</span><span>;
</span><span>16</span> <span> }
</span><span>17</span> <span> }
</span><span>18</span> <span>else</span>
<span>19</span> <span> {
</span><span>20</span> data[j + <span>1</span>] =<span> temp;
</span><span>21</span> <span>break</span><span>;
</span><span>22</span> <span> }
</span><span>23</span> <span> }
</span><span>24</span> <span> }
</span><span>25</span> }

过程解析:将要排序的数(索引为i)存储起来,向前查找合适位置j+1,将i-1到j+1的元素依次向后

移动一位,空出j+1,然后将之前存储的值放在这个位置。

这个方法写的不如维基上的简洁清晰,由于合适位置是j+1所以多出了对j==0的判断,但实际效率影响无差别。

建议比照维基和我写的排序,自行选择。

二分查找法优化插入排序

插入排序主要工作是在有序的数列中对要排序的数查找合适的位置,而查找里面经典的二分查找法正可以适用。

原理:通过二分查找法的方式找到一个位置索引。当要排序的数插入这个位置时,大于前一个数,小于后一个数。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> InsertSortImprovedWithBinarySearch(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>int</span><span> temp;
</span><span> 4</span> <span>int</span><span> tempIndex;
</span><span> 5</span> <span>for</span> (<span>int</span> i = <span>1</span>; i &lt; data.Count; i++<span>)
</span><span> 6</span> <span> {
</span><span> 7</span> temp =<span> data[i];
</span><span> 8</span> tempIndex = BinarySearchForInsertSort(data, <span>0</span><span>, i, i);
</span><span> 9</span> <span>for</span> (<span>int</span> j = i - <span>1</span>; j &gt;= tempIndex; j--<span>)
</span><span>10</span> <span> {
</span><span>11</span> data[j + <span>1</span>] =<span> data[j];
</span><span>12</span> <span> }
</span><span>13</span> data[tempIndex] =<span> temp;
</span><span>14</span> <span> }
</span><span>15</span> <span> }
</span><span>16</span>
<span>17</span> <span>public</span> <span>static</span> <span>int</span> BinarySearchForInsertSort(IList&lt;<span>int</span>&gt; data, <span>int</span> low, <span>int</span> high, <span>int</span><span> key)
</span><span>18</span> <span> {
</span><span>19</span> <span>if</span> (low &gt;= data.Count - <span>1</span><span>)
</span><span>20</span> <span>return</span> data.Count - <span>1</span><span>;
</span><span>21</span> <span>if</span> (high &lt;= <span>0</span><span>)
</span><span>22</span> <span>return</span> <span>0</span><span>;
</span><span>23</span> <span>int</span> mid = (low + high) / <span>2</span><span>;
</span><span>24</span> <span>if</span> (mid == key) <span>return</span><span> mid;
</span><span>25</span> <span>if</span> (data[key] &gt;<span> data[mid])
</span><span>26</span> <span> {
</span><span>27</span> <span>if</span> (data[key] &lt; data[mid + <span>1</span><span>])
</span><span>28</span> <span>return</span> mid + <span>1</span><span>;
</span><span>29</span> <span>return</span> BinarySearchForInsertSort(data, mid + <span>1</span><span>, high, key);
</span><span>30</span> <span> }
</span><span>31</span> <span>else <span>// data[key] &lt;= data[mid]</span></span>
<span>32</span> <span> {
</span><span>33</span> <span>if</span> (mid - <span>1</span> &lt; <span>0</span>) <span>return</span> <span>0</span><span>;
</span><span>34</span> <span>if</span> (data[key] &gt; data[mid - <span>1</span><span>])
</span><span>35</span> <span>return</span><span> mid;
</span><span>36</span> <span>return</span> BinarySearchForInsertSort(data, low, mid - <span>1</span><span>, key);
</span><span>37</span> <span> }
</span><span>38</span> }

 过程解析:需要注意的是二分查找方法实现中high-low==1的时候mid==low,所以需要33行

mid-1<0即mid==0的判断,否则下行会索引越界。

快速排序

快速排序是一种有效比较较多的高效排序。它包含了“分而治之”以及“哨兵”的思想。

原理:从数列中挑选一个数作为“哨兵”,使比它小的放在它的左侧,比它大的放在它的右侧。将要排序是数列递归地分割到

最小数列,每次都让分割出的数列符合“哨兵”的规则,自然就将数列变得有序。 维基入口

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> QuickSortStrict(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> QuickSortStrict(data, <span>0</span>, data.Count - <span>1</span><span>);
</span><span> 4</span> <span> }
</span><span> 5</span>
<span> 6</span> <span>public</span> <span>static</span> <span>void</span> QuickSortStrict(IList&lt;<span>int</span>&gt; data, <span>int</span> low, <span>int</span><span> high)
</span><span> 7</span> <span> {
</span><span> 8</span> <span>if</span> (low &gt;= high) <span>return</span><span>;
</span><span> 9</span> <span>int</span> temp =<span> data[low];
</span><span>10</span> <span>int</span> i = low + <span>1</span>, j =<span> high;
</span><span>11</span> <span>while</span> (<span>true</span><span>)
</span><span>12</span> <span> {
</span><span>13</span> <span>while</span> (data[j] &gt; temp) j--<span>;
</span><span>14</span> <span>while</span> (data[i] &lt; temp &amp;&amp; i &lt; j) i++<span>;
</span><span>15</span> <span>if</span> (i &gt;= j) <span>break</span><span>;
</span><span>16</span> <span> Swap(data, i, j);
</span><span>17</span> i++; j--<span>;
</span><span>18</span> <span> }
</span><span>19</span> <span>if</span> (j !=<span> low)
</span><span>20</span> <span> Swap(data, low, j);
</span><span>21</span> QuickSortStrict(data, j + <span>1</span><span>, high);
</span><span>22</span> QuickSortStrict(data, low, j - <span>1</span><span>);
</span><span>23</span> }

过程解析:取的哨兵是数列的第一个值,然后从第二个和末尾同时查找,左侧要显示的是小于哨兵的值,

所以要找到不小于的i,右侧要显示的是大于哨兵的值,所以要找到不大于的j。将找到的i和j的数交换,

这样可以减少交换次数。i>=j时,数列全部查找了一遍,而不符合条件j必然是在小的那一边,而哨兵

是第一个数,位置本应是小于自己的数。所以将哨兵与j交换,使符合“哨兵”的规则。

这个版本的缺点在于如果是有序数列排序的话,递归次数会很可怕的。

另一个版本

这是维基上的一个C#版本,我觉得很有意思。这个版本并没有严格符合“哨兵”的规则。但却将“分而治之”

以及“哨兵”思想融入其中,代码简洁。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> QuickSortRelax(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> QuickSortRelax(data, <span>0</span>, data.Count - <span>1</span><span>);
</span><span> 4</span> <span> }
</span><span> 5</span>
<span> 6</span> <span>public</span> <span>static</span> <span>void</span> QuickSortRelax(IList&lt;<span>int</span>&gt; data, <span>int</span> low, <span>int</span><span> high)
</span><span> 7</span> <span> {
</span><span> 8</span> <span>if</span> (low &gt;= high) <span>return</span><span>;
</span><span> 9</span> <span>int</span> temp = data[(low + high) / <span>2</span><span>];
</span><span>10</span> <span>int</span> i = low - <span>1</span>, j = high + <span>1</span><span>;
</span><span>11</span> <span>while</span> (<span>true</span><span>)
</span><span>12</span> <span> {
</span><span>13</span> <span>while</span> (data[++i] &lt;<span> temp) ;
</span><span>14</span> <span>while</span> (data[--j] &gt;<span> temp) ;
</span><span>15</span> <span>if</span> (i &gt;= j) <span>break</span><span>;
</span><span>16</span> <span> Swap(data, i, j);
</span><span>17</span> <span> }
</span><span>18</span> QuickSortRelax(data, j + <span>1</span><span>, high);
</span><span>19</span> QuickSortRelax(data, low, i - <span>1</span><span>);
</span><span>20</span> }

过程解析:取的哨兵是数列中间的数。将数列分成两波,左侧小于等于哨兵,右侧大于等于哨兵。

也就是说,哨兵不一定处于两波数的中间。虽然哨兵不在中间,但不妨碍“哨兵”的思想的实现。所以

这个实现也可以达到快速排序的效果。但却造成了每次递归完成,要排序的数列数总和没有减少(除非i==j)。

针对这个版本的缺点,我进行了优化

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> QuickSortRelaxImproved(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> QuickSortRelaxImproved(data, <span>0</span>, data.Count - <span>1</span><span>);
</span><span> 4</span> <span> }
</span><span> 5</span>
<span> 6</span> <span>public</span> <span>static</span> <span>void</span> QuickSortRelaxImproved(IList&lt;<span>int</span>&gt; data, <span>int</span> low, <span>int</span><span> high)
</span><span> 7</span> <span> {
</span><span> 8</span> <span>if</span> (low &gt;= high) <span>return</span><span>;
</span><span> 9</span> <span>int</span> temp = data[(low + high) / <span>2</span><span>];
</span><span>10</span> <span>int</span> i = low - <span>1</span>, j = high + <span>1</span><span>;
</span><span>11</span> <span>int</span> index = (low + high) / <span>2</span><span>;
</span><span>12</span> <span>while</span> (<span>true</span><span>)
</span><span>13</span> <span> {
</span><span>14</span> <span>while</span> (data[++i] &lt;<span> temp) ;
</span><span>15</span> <span>while</span> (data[--j] &gt;<span> temp) ;
</span><span>16</span> <span>if</span> (i &gt;= j) <span>break</span><span>;
</span><span>17</span> <span> Swap(data, i, j);
</span><span>18</span> <span>if</span> (i == index) index =<span> j;
</span><span>19</span> <span>else</span> <span>if</span> (j == index) index =<span> i;
</span><span>20</span> <span> }
</span><span>21</span> <span>if</span> (j ==<span> i)
</span><span>22</span> <span> {
</span><span>23</span> QuickSortRelaxImproved(data, j + <span>1</span><span>, high);
</span><span>24</span> QuickSortRelaxImproved(data, low, i - <span>1</span><span>);
</span><span>25</span> <span> }
</span><span>26</span> <span>else</span> <span>//</span><span>i-j==1</span>
<span>27</span> <span> {
</span><span>28</span> <span>if</span> (index &gt;=<span> i)
</span><span>29</span> <span> {
</span><span>30</span> <span>if</span> (index !=<span> i)
</span><span>31</span> <span> Swap(data, index, i);
</span><span>32</span> QuickSortRelaxImproved(data, i + <span>1</span><span>, high);
</span><span>33</span> QuickSortRelaxImproved(data, low, i - <span>1</span><span>);
</span><span>34</span> <span> }
</span><span>35</span> <span>else <span>//</span><span>index &lt; i</span></span>
<span>36</span> <span> {
</span><span>37</span> <span>if</span> (index !=<span> j)
</span><span>38</span> <span> Swap(data, index, j);
</span><span>39</span> QuickSortRelaxImproved(data, j + <span>1</span><span>, high);
</span><span>40</span> QuickSortRelaxImproved(data, low, j - <span>1</span><span>);
</span><span>41</span> <span> }
</span><span>42</span> <span> }
</span><span>43</span> }

过程解析:定义了一个变量Index,来跟踪哨兵的位置。发现哨兵最后在小于自己的那堆,

那就与j交换,否则与i交换。达到每次递归都能减少要排序的数列数总和的目的。

归并排序

归并排序也是采用“分而治之”的方式。刚发现分治法是一种算法范式,我还一直以为是一种需要意会的思想呢。

不好意思了,孤陋寡闻了,哈哈!

原理:将两个有序的数列,通过比较,合并为一个有序数列。 维基入口

为方便理解,此处实现用了List的一些方法,随后有IList版本。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<span> 1</span>         <span>public</span> <span>static</span> List&lt;<span>int</span>&gt; MergeSortOnlyList(List&lt;<span>int</span>&gt; data, <span>int</span> low, <span>int</span><span> high)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>if</span> (low ==<span> high)
</span><span> 4</span> <span>return</span> <span>new</span> List&lt;<span>int</span>&gt;<span> { data[low] };
</span><span> 5</span> List&lt;<span>int</span>&gt; mergeData = <span>new</span> List&lt;<span>int</span>&gt;<span>();
</span><span> 6</span> <span>int</span> mid = (low + high) / <span>2</span><span>;
</span><span> 7</span> List&lt;<span>int</span>&gt; leftData =<span> MergeSortOnlyList(data, low, mid);
</span><span> 8</span> List&lt;<span>int</span>&gt; rightData = MergeSortOnlyList(data, mid + <span>1</span><span>, high);
</span><span> 9</span> <span>int</span> i = <span>0</span>, j = <span>0</span><span>;
</span><span>10</span> <span>while</span> (<span>true</span><span>)
</span><span>11</span> <span> {
</span><span>12</span> <span>if</span> (leftData[i] &lt;<span> rightData[j])
</span><span>13</span> <span> {
</span><span>14</span> <span> mergeData.Add(leftData[i]);
</span><span>15</span> <span>if</span> (++i ==<span> leftData.Count)
</span><span>16</span> <span> {
</span><span>17</span> mergeData.AddRange(rightData.GetRange(j, rightData.Count -<span> j));
</span><span>18</span> <span>break</span><span>;
</span><span>19</span> <span> }
</span><span>20</span> <span> }
</span><span>21</span> <span>else</span>
<span>22</span> <span> {
</span><span>23</span> <span> mergeData.Add(rightData[j]);
</span><span>24</span> <span>if</span> (++j ==<span> rightData.Count)
</span><span>25</span> <span> {
</span><span>26</span> mergeData.AddRange(leftData.GetRange(i, leftData.Count -<span> i));
</span><span>27</span> <span>break</span><span>;
</span><span>28</span> <span> }
</span><span>29</span> <span> }
</span><span>30</span> <span> }
</span><span>31</span> <span>return</span><span> mergeData;
</span><span>32</span> <span> }
</span><span>33</span>
<span>34</span> <span>public</span> <span>static</span> List&lt;<span>int</span>&gt; MergeSortOnlyList(List&lt;<span>int</span>&gt;<span> data)
</span><span>35</span> <span> {
</span><span>36</span> data = MergeSortOnlyList(data, <span>0</span>, data.Count - <span>1</span><span>); <span>//不会改变外部引用 参照<a href="http://www.cnblogs.com/fatbird/p/parametersInCsharp.html" target="_blank">C#参数传递
</a></span></span><span>37</span> <span>return</span><span> data;
</span><span>38</span> }

过程解析:将数列分为两部分,分别得到两部分数列的有序版本,然后逐个比较,将比较出的小数逐个放进

新的空数列中。当一个数列放完后,将另一个数列剩余数全部放进去。

IList版本

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<span> 1</span>         <span>public</span> <span>static</span> IList&lt;<span>int</span>&gt; MergeSort(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> data = MergeSort(data, <span>0</span>, data.Count - <span>1</span><span>);
</span><span> 4</span> <span>return</span><span> data;
</span><span> 5</span> <span> }
</span><span> 6</span>
<span> 7</span> <span>public</span> <span>static</span> IList&lt;<span>int</span>&gt; MergeSort(IList&lt;<span>int</span>&gt; data, <span>int</span> low, <span>int</span><span> high)
</span><span> 8</span> <span> {
</span><span> 9</span> <span>int</span> length = high - low + <span>1</span><span>;
</span><span>10</span> IList&lt;<span>int</span>&gt; mergeData =<span> NewInstance(data, length);
</span><span>11</span> <span>if</span> (low ==<span> high)
</span><span>12</span> <span> {
</span><span>13</span> mergeData[<span>0</span>] =<span> data[low];
</span><span>14</span> <span>return</span><span> mergeData;
</span><span>15</span> <span> }
</span><span>16</span> <span>int</span> mid = (low + high) / <span>2</span><span>;
</span><span>17</span> IList&lt;<span>int</span>&gt; leftData =<span> MergeSort(data, low, mid);
</span><span>18</span> IList&lt;<span>int</span>&gt; rightData = MergeSort(data, mid + <span>1</span><span>, high);
</span><span>19</span> <span>int</span> i = <span>0</span>, j = <span>0</span><span>;
</span><span>20</span> <span>while</span> (<span>true</span><span>)
</span><span>21</span> <span> {
</span><span>22</span> <span>if</span> (leftData[i] &lt;<span> rightData[j])
</span><span>23</span> <span> {
</span><span>24</span> mergeData[i + j] = leftData[i++]; <span>//</span><span>不能使用Add,Array Length不可变</span>
<span>25</span> <span>if</span> (i ==<span> leftData.Count)
</span><span>26</span> <span> {
</span><span>27</span> <span>int</span> rightLeft = rightData.Count -<span> j;
</span><span>28</span> <span>for</span> (<span>int</span> m = <span>0</span>; m &lt; rightLeft; m++<span>)
</span><span>29</span> <span> {
</span><span>30</span> mergeData[i + j] = rightData[j++<span>];
</span><span>31</span> <span> }
</span><span>32</span> <span>break</span><span>;
</span><span>33</span> <span> }
</span><span>34</span> <span> }
</span><span>35</span> <span>else</span>
<span>36</span> <span> {
</span><span>37</span> mergeData[i + j] = rightData[j++<span>];
</span><span>38</span> <span>if</span> (j ==<span> rightData.Count)
</span><span>39</span> <span> {
</span><span>40</span> <span>int</span> leftleft = leftData.Count -<span> i;
</span><span>41</span> <span>for</span> (<span>int</span> n = <span>0</span>; n &lt; leftleft; n++<span>)
</span><span>42</span> <span> {
</span><span>43</span> mergeData[i + j] = leftData[i++<span>];
</span><span>44</span> <span> }
</span><span>45</span> <span>break</span><span>;
</span><span>46</span> <span> }
</span><span>47</span> <span> }
</span><span>48</span> <span> }
</span><span>49</span> <span>return</span><span> mergeData;
</span><span>50</span>
<span>51</span> }

过程原理与上个一样,此处就不赘述了。

堆排序

堆排序是根据堆这种数据结构设计的一种算法。堆的特性:父节点的值总是小于(或大于)它的子节点。近似二叉树。

原理:将数列构建为最大堆数列(即父节点总是最大值),将最大值(即根节点)交换到数列末尾。这样要排序的数列数总和减少,

同时根节点不再是最大值,调整最大堆数列。如此重复,最后得到有序数列。 维基入口   有趣的演示

实现准备:如何将数列构造为堆——父节点i的左子节点为2i+1,右子节点为2i+2。节点i的父节点为floor((i-1)/2)。

实现如下(这个实现判断和临时变量使用太多,导致效率低,评论中@小城故事提出了更好的实现):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> HeapSort(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span> BuildMaxHeapify(data);
</span><span> 4</span> <span>int</span> j =<span> data.Count;
</span><span> 5</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt;<span> j; )
</span><span> 6</span> <span> {
</span><span> 7</span> Swap(data, i, --<span>j);
</span><span> 8</span> <span>if</span> (j - <span>2</span> &lt; <span>0</span><span>) <span>//只剩下1个数 j代表余下要排列的数的个数
</span></span><span> 9</span> <span>break</span><span>;
</span><span>10</span> <span>int</span> k = <span>0</span><span>;
</span><span>11</span> <span>while</span> (<span>true</span><span>)
</span><span>12</span> <span> {
</span><span>13</span> <span>if</span> (k &gt; (j - <span>2</span>) / <span>2</span>) <span>break</span><span>; <span>//即:k &gt; ((j-1)-1)/2</span> <span>超出最后一个父节点的位置
</span></span><span>14</span> <span>else</span>
<span>15</span> <span> {
</span><span>16</span> <span>int</span> temp =<span> k;
</span><span>17</span> k = ReSortMaxBranch(data, k, <span>2</span> * k + <span>1</span>, <span>2</span> * k + <span>2</span>, j - <span>1</span><span>);
</span><span>18</span> <span>if</span> (temp == k) <span>break</span><span>;
</span><span>19</span> <span> }
</span><span>20</span> <span> }
</span><span>21</span> <span> }
</span><span>22</span> <span> }
</span><span>23</span>
<span>24</span> <span>public</span> <span>static</span> <span>void</span> BuildMaxHeapify(IList&lt;<span>int</span>&gt;<span> data)
</span><span>25</span> <span> {
</span><span>26</span> <span>for</span> (<span>int</span> i = data.Count / <span>2</span> - <span>1</span>; i &gt;= <span>0</span>; i--<span>) <span>//(data.Count-1)-1)/2为数列最大父节点索引
</span></span><span>27</span> <span> {
</span><span>28</span> <span>int</span> temp =<span> i;
</span><span>29</span> temp = ReSortMaxBranch(data, i, <span>2</span> * i + <span>1</span>, <span>2</span> * i + <span>2</span>, data.Count - <span>1</span><span>);
</span><span>30</span> <span>if</span> (temp !=<span> i)
</span><span>31</span> <span> {
</span><span>32</span> <span>int</span> k =<span> i;
</span><span>33</span> <span>while</span> (k != temp &amp;&amp; temp &lt;= data.Count / <span>2</span> - <span>1</span><span>)
</span><span>34</span> <span> {
</span><span>35</span> k =<span> temp;
</span><span>36</span> temp = ReSortMaxBranch(data, temp, <span>2</span> * temp + <span>1</span>, <span>2</span> * temp + <span>2</span>, data.Count - <span>1</span><span>);
</span><span>37</span> <span> }
</span><span>38</span> <span> }
</span><span>39</span> <span> }
</span><span>40</span> <span> }
</span><span>41</span>
<span>42</span> <span>public</span> <span>static</span> <span>int</span> ReSortMaxBranch(IList&lt;<span>int</span>&gt; data, <span>int</span> maxIndex, <span>int</span> left, <span>int</span> right, <span>int</span><span> lastIndex)
</span><span>43</span> <span> {
</span><span>44</span> <span>int</span><span> temp;
</span><span>45</span> <span>if</span> (right &gt;<span> lastIndex) <span>//父节点只有一个子节点
</span></span><span>46</span> temp =<span> left;
</span><span>47</span> <span>else</span>
<span>48</span> <span> {
</span><span>49</span> <span>if</span> (data[left] &gt;<span> data[right])
</span><span>50</span> temp =<span> left;
</span><span>51</span> <span>else</span> temp =<span> right;
</span><span>52</span> <span> }
</span><span>53</span>
<span>54</span> <span>if</span> (data[maxIndex] &lt;<span> data[temp])
</span><span>55</span> <span> Swap(data, maxIndex, temp);
</span><span>56</span> <span>else</span> temp =<span> maxIndex;
</span><span>57</span> <span>return</span><span> temp;
</span><span>58</span> }

过程解析:BuildMaxHeapify为排序前构建的最大堆数列方法,主要内容为从最后一个父节点开始往前将每个三角组合

(即父节点与它的两个子节点)符合父节点值最大的规则。ReSortMaxBranch为将三角调整为父节点值最大,

并返回该值之前的索引,用来判断是否进行了交换,以及原来的父节点值交换到了什么位置。在HeapSort里首先

构建了最大堆数列,然后将根节点交换到末尾,根节点不是最大值了,在while语句中对最大堆数列进行调整。

插曲:自从看了Martin Fowler大师《重构》第三版,我发现我更不喜欢写注释了。每次都想着尽量让方法的名字更贴切,

即使会造成方法的名字很长很丑。这算不算曲解了大师的意思啊!?上面的代码注释都是写博客的时候现加的(源代码很干净的。汗!)。

希尔排序

希尔排序是插入排序的一种更高效的改进版本。

在前面介绍的插入排序,我们知道1.它对有序数列排序的效率是非常高的 2.要排序的数向前移动是一步步进行的导致插入排序效率低。

希尔排序正是利用第一点,改善第二点,达到更理想的效果。

原理:通过奇妙的步长,插入排序间隔步长的元素,随后逐渐缩短步长至1,实现数列的插入排序。 维基入口

疑问:可以想象到排序间隔步长的数,会逐渐让数列变得有序,提升最后步长为1时标准插入排序的效率。在维基上看到这么

一句话“可能希尔排序最重要的地方在于当用较小步长排序后,以前用的较大步长仍然是有序的”注意用词是‘可能’。我的疑问是

这是个正确的命题吗?如何证明呢?看维基上也是由果推因,说是如果不是这样,就不会排序那么快了。可这我感觉还是太牵强了,

哪位大哥发现相关资料,希望能分享出来,不胜感激。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> ShellSort(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>int</span><span> temp;
</span><span> 4</span> <span>for</span> (<span>int</span> gap = data.Count / <span>2</span>; gap &gt; <span>0</span>; gap /= <span>2</span><span>)
</span><span> 5</span> <span> {
</span><span> 6</span> <span>for</span> (<span>int</span> i = gap; i &lt; data.Count; i +=<span> gap)
</span><span> 7</span> <span> {
</span><span> 8</span> temp =<span> data[i];
</span><span> 9</span> <span>for</span> (<span>int</span> j = i - gap; j &gt;= <span>0</span>; j -=<span> gap)
</span><span>10</span> <span> {
</span><span>11</span> <span>if</span> (data[j] &gt;<span> temp)
</span><span>12</span> <span> {
</span><span>13</span> data[j + gap] =<span> data[j];
</span><span>14</span> <span>if</span> (j == <span>0</span><span>)
</span><span>15</span> <span> {
</span><span>16</span> data[j] =<span> temp;
</span><span>17</span> <span>break</span><span>;
</span><span>18</span> <span> }
</span><span>19</span> <span> }
</span><span>20</span> <span>else</span>
<span>21</span> <span> {
</span><span>22</span> data[j + gap] =<span> temp;
</span><span>23</span> <span>break</span><span>;
</span><span>24</span> <span> }
</span><span>25</span> <span> }
</span><span>26</span> <span> }
</span><span>27</span> <span> }
</span><span>28</span> }

过程解析:采用的步长是N/2,每次取半,直至1。循环内部就是标准的插入排序。

——————

修正:修正后希尔排序才是真正牛叉的希尔啊!感谢@390218462的提出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> ShellSortCorrect(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>int</span><span> temp;
</span><span> 4</span> <span>for</span> (<span>int</span> gap = data.Count / <span>2</span>; gap &gt; <span>0</span>; gap /= <span>2</span><span>)
</span><span> 5</span> <span> {
</span><span> 6</span> <span>for</span> (<span>int</span> i = gap; i &lt; data.Count; <span>i++</span><span>) // <span>i+ = gap 改为了 i++
</span></span><span> 7</span> <span> {
</span><span> 8</span> temp =<span> data[i];
</span><span> 9</span> <span>for</span> (<span>int</span> j = i - gap; j &gt;= <span>0</span>; j -=<span> gap)
</span><span>10</span> <span> {
</span><span>11</span> <span>if</span> (data[j] &gt;<span> temp)
</span><span>12</span> <span> {
</span><span>13</span> data[j + gap] =<span> data[j];
</span><span>14</span> <span>if</span> (j == <span>0</span><span>)
</span><span>15</span> <span> {
</span><span>16</span> data[j] =<span> temp;
</span><span>17</span> <span>break</span><span>;
</span><span>18</span> <span> }
</span><span>19</span> <span> }
</span><span>20</span> <span>else</span>
<span>21</span> <span> {
</span><span>22</span> data[j + gap] =<span> temp;
</span><span>23</span> <span>break</span><span>;
</span><span>24</span> <span> }
</span><span>25</span> <span> }
</span><span>26</span> <span> }
</span><span>27</span> <span> }
</span><span>28</span> }

——————

这里实现的貌似是最差的希尔排序。主要源于步长的选择。维基上有各种牛叉的“凌波微步”,极限在哪里,

喜欢挑战的同学可以去学习学习。看维基排序算法里六种排序的测试,希尔最快,比快速排序还快!!我没实现啊!

只是对于神奇的步长更充满了敬畏。

基数排序

基数排序是一种非比较型整数排序。

“非比较型”是什么意思呢?因为它内部使用的是桶排序,而桶排序是非比较型排序。

这里就要说说桶排序了。一个非常有意思的排序。

桶排序

原理:取一定数量(数列中的最大值)的编好序号的桶,将数列每个数放进编号为它的桶里,然后将不是空的桶依次倒出来,

就组成有序数列了。  维基入口

好吧!聪明的人一眼就看出桶排序的破绽了。假设只有两个数1,10000,岂不是要一万个桶!?这确实是个问题啊!我也

没想出解决办法。我起初也以为桶排序就是一个通过牺牲空间来换取时间的排序算法,它不需要比较,所以是非比较型算法。

但看了有趣的演示桶排序后,发现世界之大,你没有解决,不代表别人没解决,睿智的人总是很多。

1,9999的桶排序实现:new Int[2];总共有两个数,得出最大数9999的位数4,取10的4次幂即10000作为分母,

要排序的数(1或9999)作为分子,并乘以数列总数2,即1*2/10000,9999*2/10000得到各自的位置0,1,完成排序。

如果是1,10000进行排序的话,上面的做法就需要稍微加一些处理——发现最大数是10的n次幂,就将它作为分母,并

放在数列末尾就好了。

如果是9999,10000进行排序的话,那就需要二维数组了,两个都在位置1,位置0没数。这个时候就需要在放

入每个位置时采用其它排序(比如插入排序)办法对这个位置的多个数排序了。

为基数排序做个过渡,我这里实现了一个个位数桶排序

涉及到了当重复的数出现的处理。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<span> 1</span>         <span>public</span> <span>static</span> <span>void</span> BucketSortOnlyUnitDigit(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>int</span>[] indexCounter = <span>new</span> <span>int</span>[<span>10</span><span>];
</span><span> 4</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; data.Count; i++<span>)
</span><span> 5</span> <span> {
</span><span> 6</span> indexCounter[data[i]]++<span>;
</span><span> 7</span> <span> }
</span><span> 8</span> <span>int</span>[] indexBegin = <span>new</span> <span>int</span>[<span>10</span><span>];
</span><span> 9</span> <span>for</span> (<span>int</span> i = <span>1</span>; i &lt; <span>10</span>; i++<span>)
</span><span>10</span> <span> {
</span><span>11</span> indexBegin[i] = indexBegin[i-1]+<span> indexCounter[i-1];
</span><span>15</span> <span> }
</span><span>16</span> IList&lt;<span>int</span>&gt; tempList =<span> NewInstance(data, data.Count);
</span><span>17</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; data.Count; i++<span>)
</span><span>18</span> <span> {
</span><span>19</span> <span>int</span> number =<span> data[i];
</span><span>20</span> tempList[indexBegin[number]++] =<span> data[i];
</span><span>21</span> <span> }
</span><span>22</span> data =<span> tempList;
</span><span>23</span> }

过程解析:indexCounter进行对每个数出现的频率的统计。indexBegin存储每个数的起始索引。

比如 1 1 2,indexCounter统计到0个0,2个1,1个2。indexBegin计算出0,1,2的起始索引分别为

0,0,2。当1个1已取出排序,那索引将+1,变为0,1,2。这样就通过提前给重复的数空出位置,解决了

重复的数出现的问题。当然,你也可以考虑用二维数组来解决重复。

下面继续基数排序。

基数排序原理:将整数按位数切割成不同的数字,然后按每个位数分别比较。

取得最大数的位数,从低位开始,每个位上进行桶排序。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<span> 1</span>         <span>public</span> <span>static</span> IList&lt;<span>int</span>&gt; RadixSort(IList&lt;<span>int</span>&gt;<span> data)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>int</span> max = data[<span>0</span><span>];
</span><span> 4</span> <span>for</span> (<span>int</span> i = <span>1</span>; i &lt; data.Count; i++<span>)
</span><span> 5</span> <span> {
</span><span> 6</span> <span>if</span> (data[i] &gt;<span> max)
</span><span> 7</span> max =<span> data[i];
</span><span> 8</span> <span> }
</span><span> 9</span> <span>int</span> digit = <span>1</span><span>;
</span><span>10</span> <span>while</span> (max / <span>10</span> != <span>0</span><span>)
</span><span>11</span> <span> {
</span><span>12</span> digit++<span>;
</span><span>13</span> max /= <span>10</span><span>;
</span><span>14</span> <span> }
</span><span>15</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; digit; i++<span>)
</span><span>16</span> <span> {
</span><span>17</span> <span>int</span>[] indexCounter = <span>new</span> <span>int</span>[<span>10</span><span>];
</span><span>18</span> IList&lt;<span>int</span>&gt; tempList =<span> NewInstance(data, data.Count);
</span><span>19</span> <span>for</span> (<span>int</span> j = <span>0</span>; j &lt; data.Count; j++<span>)
</span><span>20</span> <span> {
</span><span>21</span> <span>int</span> number = (data[j] % Convert.ToInt32(Math.Pow(<span>10</span>, i + <span>1</span>))) / Convert.ToInt32(Math.Pow(<span>10</span>, i)); <span>//</span><span>得出i+1位上的数</span>
<span>22</span> indexCounter[number]++<span>;
</span><span>23</span> <span> }
</span><span>24</span> <span>int</span>[] indexBegin = <span>new</span> <span>int</span>[<span>10</span><span>];
</span><span>25</span> <span>for</span> (<span>int</span> k = <span>1</span>; k &lt; <span>10</span>; k++<span>)
</span><span>26</span> <span> {
</span><span>27</span> indexBegin[k] = indexBegin[k - <span>1</span>] + indexCounter[k - <span>1</span><span>];
</span><span>28</span> <span> }
</span><span>29</span> <span>for</span> (<span>int</span> k = <span>0</span>; k &lt; data.Count; k++<span>)
</span><span>30</span> <span> {
</span><span>31</span> <span>int</span> number = (data[k] % Convert.ToInt32(Math.Pow(<span>10</span>, i + <span>1</span>))) / Convert.ToInt32(Math.Pow(<span>10</span><span>, i));
</span><span>32</span> tempList[indexBegin[number]++] =<span> data[k];
</span><span>33</span> <span> }
</span><span>34</span> data =<span> tempList;
</span><span>35</span> <span> }
</span><span>36</span> <span>return</span><span> data;
</span><span>37</span> }

过程解析:得出最大数的位数,从低位开始桶排序。我写的这个实现代码并不简洁,但逻辑更清晰。

后面测试的时候我们就会发现,按理来说这个实现也还行吧! 但并不如想象的那么快!

循环的次数太多?(统计频率n次+9次计算+n次放到新的数组)*位数。

创建的新实例太多?(new int[10]两次+NewInstance is反射判断创建实例+new int[n])*位数

测试比较

添加随机数组,数组有序校验,微软Linq排序

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<span> 1</span>         <span>public</span> <span>static</span> <span>int</span>[] RandomSet(<span>int</span> length, <span>int</span><span> max)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>int</span>[] result = <span>new</span> <span>int</span><span>[length];
</span><span> 4</span> Random rand = <span>new</span><span> Random();
</span><span> 5</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; result.Length; i++<span>)
</span><span> 6</span> <span> {
</span><span> 7</span> result[i] =<span> rand.Next(max);
</span><span> 8</span> <span> }
</span><span> 9</span> <span>return</span><span> result;
</span><span>10</span> <span> }
</span><span>11</span>
<span>12</span> <span>public</span> <span>static</span> <span>bool</span> IsAscOrdered(IList&lt;<span>int</span>&gt;<span> data)
</span><span>13</span> <span> {
</span><span>14</span> <span>bool</span> flag = <span>true</span><span>;
</span><span>15</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; data.Count - <span>1</span>; i++<span>)
</span><span>16</span> <span> {
</span><span>17</span> <span>if</span> (data[i] &gt; data[i + <span>1</span><span>])
</span><span>18</span> flag = <span>false</span><span>;
</span><span>19</span> <span> }
</span><span>20</span> <span>return</span><span> flag;
</span><span>21</span> <span> }
</span><span>22</span>
<span>23</span> <span>public</span> <span>static</span> <span>void</span> TestMicrosoft(IList&lt;<span>int</span>&gt;<span> data)
</span><span>24</span> <span> {
</span><span>25</span> Stopwatch stopwatch = <span>new</span><span> Stopwatch();
</span><span>26</span> <span> stopwatch.Start();
</span><span>27</span> List&lt;<span>int</span>&gt; result = data.OrderBy(a =&gt;<span> a).ToList();
</span><span>28</span> <span> stopwatch.Stop();
</span><span>29</span> <span>string</span> methodName = <span>"</span><span>TestMicrosoft</span><span>"</span><span>;
</span><span>30</span> <span>int</span> length =<span> methodName.Length;
</span><span>31</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; <span>40</span> - length; i++<span>)
</span><span>32</span> <span> {
</span><span>33</span> methodName += <span>"</span> <span>"</span><span>;
</span><span>34</span> <span> }
</span><span>35</span> Console.WriteLine(methodName +
<span>36</span> <span>"</span><span> IsAscOrdered:</span><span>"</span> + IsAscOrdered(result) + <span>"</span><span> Time:</span><span>"</span> +<span> stopwatch.Elapsed.TotalSeconds);
</span><span>37</span>
<span>38</span> }

测试主体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<span> 1</span>         <span>static</span> <span>void</span> Main(<span>string</span><span>[] args)
</span><span> 2</span> <span> {
</span><span> 3</span> <span>int</span>[] aa = RandomSet(<span>50000</span>, <span>99999</span><span>);
</span><span> 4</span> <span>//</span><span>int[] aa = OrderedSet(5000);</span>
<span> 5</span> Console.WriteLine(<span>"</span><span>Array Length:</span><span>"</span> +<span> aa.Length);
</span><span> 6</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)SelectSort, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span> 7</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)BubbleSort, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span> 8</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)BubbleSortImprovedWithFlag, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span> 9</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)BubbleCocktailSort, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>10</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)InsertSort, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>11</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)InsertSortImprovedWithBinarySearch, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>12</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)QuickSortStrict, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>13</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)QuickSortRelax, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>14</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)QuickSortRelaxImproved, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>15</span> RunTheMethod((Func&lt;IList&lt;<span>int</span>&gt;, IList&lt;<span>int</span>&gt;&gt;)MergeSort, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>16</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)ShellSort, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>17</span> RunTheMethod((Func&lt;IList&lt;<span>int</span>&gt;, IList&lt;<span>int</span>&gt;&gt;)RadixSort, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>18</span> RunTheMethod((Action&lt;IList&lt;<span>int</span>&gt;&gt;)HeapSort, aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>19</span> TestMicrosoft(aa.Clone() <span>as</span> <span>int</span><span>[]);
</span><span>20</span> <span> Console.Read();
</span><span>21</span> <span> }
</span><span>22</span>
<span>23</span> <span>public</span> <span>static</span> <span>void</span> RunTheMethod(Func&lt;IList&lt;<span>int</span>&gt;, IList&lt;<span>int</span>&gt;&gt; method, IList&lt;<span>int</span>&gt;<span> data)
</span><span>24</span> <span> {
</span><span>25</span> Stopwatch stopwatch = <span>new</span><span> Stopwatch();
</span><span>26</span> <span> stopwatch.Start();
</span><span>27</span> IList&lt;<span>int</span>&gt; result =<span> method(data);
</span><span>28</span> <span> stopwatch.Stop();
</span><span>29</span> <span>string</span> methodName =<span> method.Method.Name;
</span><span>30</span> <span>int</span> length =<span> methodName.Length;
</span><span>31</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; <span>40</span> - length; i++<span>)
</span><span>32</span> <span> {
</span><span>33</span> methodName += <span>"</span> <span>"</span><span>;
</span><span>34</span> <span> }
</span><span>35</span> Console.WriteLine(methodName +
<span>36</span> <span>"</span><span> IsAscOrdered:</span><span>"</span> + IsAscOrdered(result) + <span>"</span><span> Time:</span><span>"</span> +<span> stopwatch.Elapsed.TotalSeconds);
</span><span>37</span> <span> }
</span><span>38</span>
<span>39</span> <span>public</span> <span>static</span> <span>void</span> RunTheMethod(Action&lt;IList&lt;<span>int</span>&gt;&gt; method, IList&lt;<span>int</span>&gt;<span> data)
</span><span>40</span> <span> {
</span><span>41</span> Stopwatch stopwatch = <span>new</span><span> Stopwatch();
</span><span>42</span> <span> stopwatch.Start();
</span><span>43</span> <span> method(data);
</span><span>44</span> <span> stopwatch.Stop();
</span><span>45</span> <span>string</span> methodName =<span> method.Method.Name;
</span><span>46</span> <span>int</span> length =<span> methodName.Length;
</span><span>47</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; <span>40</span> - length; i++<span>)
</span><span>48</span> <span> {
</span><span>49</span> methodName += <span>"</span> <span>"</span><span>;
</span><span>50</span> <span> }
</span><span>51</span> Console.WriteLine(methodName +
<span>52</span> <span>"</span><span> IsAscOrdered:</span><span>"</span> + IsAscOrdered(data) + <span>"</span><span> Time:</span><span>"</span> +<span> stopwatch.Elapsed.TotalSeconds);
</span><span>53</span> }

剩余代码折叠在此处

View Code

测试设备:win8(64位),i7-3630QM,8G内存,vs2012

测试结果:

100000,50000,10000,5000,1000,100依次是:

结果分析:可以看出在大数组的时候,微软自带排序更接近快速排序。而当数组变小时,速度却没有明显提升,甚至变得更慢,

比如1000和100。可以推断出在数组足够小的时候,比较已经不是影响这个方法主要因素。而根据它对大数组的表现。我们可以

推断出它应该用的是快速排序。反编译验证下:

在System.Linq.EnumerableSorter下。有兴趣的同学可以去看下详细实现。

维基上也有个测试。硬件没我的好。时间是我测试结果时间的几百倍。有兴趣的同学可以比较下。

在上面的测试中,我们可以看到快速最快,归并其次,冒泡最慢(维基上是希尔最快,估计使用的是某种神奇的步长)。

在我这里,以前实现的希尔还不如二分查找优化版的快,修正后希尔快了相当多,上面测试的希尔排序是以前错误的实现。

修正后的实现测试效果请点击右侧导航到希尔排序查看。希尔排序是一种神奇又有潜力的算法。步长不好会很挫!

而基数排序却是比平均时间复杂度为o(nlogn)的堆排序,归并排序,快速排序还要慢的,虽然它的平均时间复杂度为o(n)。

冒泡标识优化版对随机数列结果优化不明显,鸡尾酒版优化可以看到,但也不是很厉害。

插入排序二分查找优化版优化比较明显。我优化的快速排序QuickSortRelaxImproved优化也不明显。

以上是随机数列的测试结果,最大值为99999。

而对于有序数列,这些方法表现又会如何呢?

我这里就不演示了。本文末尾会附上demo,大家可以自行测试。

有意思的是:

我在测试有序数列的时候,QuickSortStrict方法栈溢出了(stack overflow exception)。这个异常

是让我去stackoverflow搜寻答案吗?哈哈!我确信我的方法不是无限循环。跳过一堆链接。。。我是

在测试10000个数排序的时候发生的错误。我跟踪后发现大约在9400多次递归的时候,栈溢出。找啊找

终于找见了一个类似的问题。上面说如果一个递归9000多次而没有返回值,也会报栈溢出的。而这个方法

对于10000个有序数列,确实每次减少一个数地递归,次数会超过限制。

我的算法理论不怎么好,对于时间复杂度和空间复杂度,还有稳定度,搞得也不怎么清楚,只知道个大致的

意思。各位要笔试面试的朋友可以去维基百科这个表来了解学习。

总结

我觉得使用IList更贴近数列,更能展现基本的操作。所以我的实现中都没有将它强制转化为List

或者int[]来调用微软封装的方法。这样说来,题目说C#实现倒快有点名不副实了。不过这样却也方便了其它语言

朋友。比如将我这篇博文里的实现随便改改,就可以说是另一个语言版本的8种排序算法了。哈哈!在这里,

我想说下这次学习排序对我的意义:老久不怎么动脑了,突然动起来,磨磨唧唧地得出结果,最后倒也有点成就感!

在学习过程中,经常会脑子转不过弯,想不通的,只是走在路上或者睡觉前突然灵感一现,有点小惊喜的感觉!

这大概就是进步的特征吧!哈哈!这次写demo+写博客花费了不少时间,倒也收获颇多,尤其在我将8种

排序都实现之前,没进行过一次测试,全部实现完成后,测试时各种索引越界+无限循环+各种问题,没几个

能跑通的,到后来的几乎都没有问题,也算是锻炼了思维,找出错原因的能力。本篇是自我学习的一个总结,

要学习及锻炼的园友,还望一定自己实现一下,可以和我的比较一下,解除疑惑或者提出改进。

主要参考:维基百科有趣的演示

Demo源码

PS:我打算三月份去广州发展,主要会Asp.net mvc+jquery(不介意学习新的技术[除了webform]及语言[除了java])。