码迷,mamicode.com
首页 > 编程语言 > 详细

Chapter 2 算法基础

时间:2015-01-27 00:24:23      阅读:266      评论:0      收藏:0      [点我收藏+]

标签:

-------------------注明----------------

以下内容来自于《算法导论》           lz新手,存在各种错误以及各种不合理的地方望大家指出

----------------------------------------

2.1 插入排序

核心: 将一个数插入到已经排好序的有序数列中,从而得到一个新的、个数加1的有序数列

 形象描述:有两堆牌,左手一堆已经排好序,右手一堆未排好,将右手中的牌一张一张取出来,放到左手这堆牌中(保证每次放进去都使左手牌仍有序)

实现代码(以下按升序排序):为了以后偷懒,采用了泛型

        static void InsertSort<T>(List<T> array) where T:IComparable
        {
            for(int i=1;i<array.Count;i++)   //将第i个数插入已排好序的[0...i-1]中
            {
                T key = array[i];
                int position = i;  //用于记录将要插入的位置
                for(int j=i;j>=0;j--) //将要前面数与要插入数进行比较,如果比key大,往后移一位
                {
                    if(array[j].CompareTo(key)>0) //由于T实现了IComparable接口
                    {
                        array[j + 1] = array[j];
                        position = j;
                    }
                }
                array[position] = key;
            }
        }

 

采用的测试代码如下:(以int类型为示范)

技术分享
static void Main(string[] args)
        {
            Random rnd = new Random();
            List<int> array=new List<int>();
            for (int i = 0; i < 10;i++ )
            {
                array.Add(rnd.Next(50));
            }
            Console.WriteLine("排序前:");
            foreach (int i in array)
                Console.Write("{0} ",i);
            Console.WriteLine();
            InsertSort<int>(array);
            Console.WriteLine("排序后:");
            foreach (int i in array)
                Console.Write("{0} ", i);
            Console.ReadKey();
        }
View Code

其中一次运行结果如下:

技术分享

-----练习----

2.1-2:只需将array[j].CompareTo(key)>0改为array[j].CompareTo(key)<0即可

2.1-3:线性查找(以int为例,此外略微改动了下原题),代码如下:

技术分享
        static int FindNum(List<int> array,int num)
        {
            int position = -1;  //记录下标作为返回值,未找到则为-1
            for(int i=0;i<array.Count;i++)
            {
                if (array[i] == num)
                {
                    position = i;
                    break;    //为了返回结果为第一次找到该值的下标
                }
            }
            return position;
        }
View Code

 

2.1-4:二进制相加问题的体现,2个n位的二进制相加,结果可能依旧为n位,或者n+1位

核心:将二进制数存进数组,如0111,也按常规数组思路,从左到右代表数组的下标(即0存储在A[0]中),根据对应项相加,遇2进1的原则。

代码如下:(此代码有待改善,以下代码必须要求a,b的位数相同)

技术分享
        static List<int> AddBinary(List<int> a,List<int> b)
        {
            int[] c = new int[a.Count+1]; //数组c用于存储相加后的结果,长度为n+1
            for(int i=a.Count-1;i>=0;i--)
            {   //此处为两个二进数对应项及进位的值共同相加
                if (a[i] + b[i] + c[i + 1] >= 2)   //进位的情况
                {
                    c[i + 1] = a[i] + b[i]+c[i+1] - 2; 
                    c[i]++;
                }
                else                              //不进位的情况
                    c[i + 1] = a[i] + b[i]+c[i+1];
            }
            List<int> list = new List<int>();
            list.AddRange(c);    
            return list;                         //返回相加结果
        }
View Code

 

测试代码如下:

技术分享
        static void Main(string[] args)
        {
            int n = 6;
            Random rnd = new Random();
            List<int> a = new List<int>();
            List<int> b = new List<int>();
            for(int i=0;i<n;i++)
            {
                a.Add(rnd.Next(2));
                b.Add(rnd.Next(2));
            }
            Console.Write("二进数A=");
            foreach(int i in a)
                Console.Write("{0}",i);
            Console.WriteLine();
            Console.Write("二进数B=");
            foreach (int i in b)
                Console.Write("{0}", i);
            Console.WriteLine();
            List<int> c = AddBinary(a, b);
            Console.Write("   A+B=");
            foreach(int i in c)
                Console.Write("{0}", i);
            Console.ReadKey();
        }
View Code

 

一次测试实例结果:

 

技术分享

---------------

2.2 分析算法

判断算法优劣,根据算法所需要的资源----大部分情况下,我们采用运行时间

往往取运行时间的增长量级或增长率---即最高次作为衡量一个算法的"优劣"

 -------练习-------

2.2-2 选择排序:找出A中最小元素将其与A[0]交换位置,在找出除A[0]外最小元素与A[1]交换,一直重复到n-1

形象描述:在一堆鞋子中,找出尺码最小的往外扔,依次进行直到扔完(升序),扔完后鞋子叠起来的次序便是排好序的

排序实现代码如下:

技术分享
        static void SelectSort<T>(List<T> array)where T:IComparable
        {
            int position=0;
            for(int i=0;i<array.Count-1;i++)
            {
                position = i;              //用于记录i之后最小值的下标
                for(int j=i+1;j<array.Count;j++) //遍历找出i之后最小值
                {
                    if (array[j].CompareTo(array[position])<0)
                        position = j;
                }
                T temp = array[position];   //交换位置
                array[position] = array[i];
                array[i] = temp;
            }
        }
View Code

 

运行情况如下:

技术分享

最坏最好情况分析:由于两个for循坏无论在什么前提下,均执行次数一致(可以看到没有break等语句在Code中),所以最好最坏情况的运行时间均为θ(n^2)。

2.2-3 线性查找问题:最坏n次,最好1次。最坏和平均情况的运行时间均为θ(n)。

--------------------

2.3 设计算法

分治法:简单的说就是把问题拆拆拆(前提:拆了的只是规模比原来小)!!!拆到你感觉解起来so easy的时候那就解吧,解完后再递归回去,麻烦点的话还可能要有怎么合并的考虑。

实例:递归排序---不断对半砍直到每个都只为1个,还需要排吗?哦啦,但是还需要考虑合并的问题啦。两堆有序之间合并采用直接一一对比!

合并函数(为了能用泛型,采用不含哨兵的方法---如何对于一个T设置一个很大的值还木有想到>.<!):

        static void Merge<T>(List<T> array,int p,int q,int r)where T:IComparable
        {
            int len1 = q - p + 1, n1 = 0;  //用于临时存储两边元素的数组的参数
            int len2 = r - q, n2 = 0;
            T[] arr1 = new T[len1];   //建立两个临时数组,用于储存数组值
            T[] arr2 = new T[len2];
            for (int i = 0; i < len1; i++)  //将array值拷贝到临时存放数组
                arr1[i] = array[p + i];
            for (int j = 0; j < len2; j++)
                arr2[j] = array[j + q + 1];
            for(int k=p;k<r+1;k++)  //将两个数组中的值按升序放回到array数组中
            {
                if (arr1[n1].CompareTo(arr2[n2]) < 0)
                    array[k] = arr1[n1++];
                else
                    array[k] = arr2[n2++];
                if(n1==len1)       //此处为arr1数组已全部取完后的处理方法
                {
                    for(int m=k+1;m<r+1;m++)
                        array[m] = arr2[n2++];
                    break;
                }
                if(n2==len2)
                {
                    for (int m = k+1; m < r + 1; m++)
                        array[m] = arr1[n1++];
                    break;
                }
            }           
        }

 

 

递归排序函数:

        static void MergeSort<T>(List<T> array, int p, int r) where T : IComparable
        {
            if(p<r)
            {
                int q = (p + r) / 2;
                MergeSort(array, p, q);
                MergeSort(array, q + 1, r);
                Merge(array, p, q, r);
            }
        }

 

运行时间为O(nlogn)---由于合并函数的缘故,对于数量少的情况反而会更加耗时

-------练习-------

2.3-3 显然当n=2时,T(2)=2lg2=2---成立

         假设当n=2k时,T(2k)=2klg(2k)成立,只需证T(2k+1)=2k+1=2k+1lg2k+1

 

         由  T(2k+1)=2T(2k)+2k+1=2*2klg(2k)+2k+1=2k+1(lg2k+lg2)=2k+1lg2k+1

         从而得证。

2.3-4 插入排序的递归模式,插入函数+递归函数

插入函数如下:(和插入排序进行对比,少了个for语句---由递归完成)

技术分享
        static void Insert(List<int> array,int p,int q)
        {
            int temp = array[q], position = q;  //将array[q]插入到p..q-1中,使之依然满足升序
            for(int i=q-1;i>=p;i--)
            {
                if (array[i] > temp)
                {
                    array[i + 1] = array[i];
                    position = i;
                }
                else                          //为了减少运行步数,此处采用了break
                    break;
            }
            array[position] = temp;
        }
View Code

 

递归函数

技术分享
        static void InsertMerge(List<int> array,int p,int r)
        {
            int q;
            if(p<r)
            {
                q = r - 1;
                InsertMerge(array, p, q);
                Insert(array, p, r); //此处注意为r,如果改为q的话,则最后一项未调用排序函数
            }
        }
View Code

 

最坏运行时间的递归式为:技术分享

2.3-5 二分查找(以下Code只适用于已排好序的情况)

技术分享
        static int BinarySearch(List<int> array,int p,int r,int num)
        {
            int q = (r + p) / 2;
            if (num == array[q])
                return q;
            else if (num > array[q]&&q<r)
                return BinarySearch(array, q+1, r, num);  
            else if(num<array[q]&&q>p)
                return BinarySearch(array, p, q-1, num);
            return -1;
        }
View Code

 

2.3-6 不能,原因:利用二分查找改良的插入排序:但是由于插入位置后续的数组均要向后移动,因此最坏情况运行时间仍旧为θ(n2)

2.3-7 确定n个整数的集合S中是否存在两个数之和刚好为x的元素

method1:先将S中集合进行排序,采用递归或者后续其他排序(时间为θ(nlgn)),然后再利用二分查找+for进行寻找(该方法时间也为θ(nlgn))

递归排序代码与前面的完全相同,略过

二分查找+遍历循坏:

技术分享
        static List<int> BinaryFind(List<int> array,int num)
        {
            List<int> list = new List<int>(2);
            for(int i=0;i<array.Count-1;i++)   //将0...n-1分别与后续的进行相加
            {
                int begin = i + 1,end = array.Count - 1;  //进行二分查找的首尾
                int mid=(begin+end)/2;              
                while(begin<=end)                        //循环的终止条件,即二分查找的终止条件
                {
                    if (array[i] + array[mid] == num)    //找到的情况
                    {
                        list.Add(array[i]);
                        list.Add(array[mid]);
                        return list;
                    }
                    else if (array[i] + array[mid] > num) 
                        end = mid-1;
                    else
                        begin = mid+1;
                    mid = (begin + end) / 2;
                }
            }
            list.Add(-1); list.Add(-1);  //找不到的情况
            return list;
        }
View Code

 

method2:此方法来源于一篇博客(当时做的笔记,未写引用,忘见谅)

  step 1:对集合S进行排序,可以采用归并排序算法

  step 2:对S中每一个元素a,将b=x-a构造一个新的集合S‘,并对S’进行排序

  step 3:去除S和S‘中重复的数据

  step 4:将S和S‘按照大小进行归并,组成新的集合T,若干T中有两队及以上两个连续相等数据,说明集合S中存在两个整数其和等于x。

  • 例如:S={7,10,5,4,2,5},设x=11,执行过程如下:
  • 对S进行排序,S={2,4,5,5,7,10}。
  • S‘={9,7,6,6,4,1},排序后S’={1,4,6,6,7,9}。
  • 去除S和S‘中重复的数据后S={2,4,5,7,10},S‘={1,4,6,7,9}

归纳S和S‘组成新集合T={1,2,4,4,5,6,7,7,9,10},可以看出集合T中存在两对连续相等数据4和7,二者存在集合S中,满足4+7=11。

step 1根据上述递归排序,step2~step4的Code如下:

技术分享
        static List<int> FindSum(List<int> array,int num)
        {
            List<int> arr = new List<int>();
            List<int> list = new List<int>();
            for (int i = 0; i < array.Count; i++) //一个新数组,每个元素b[i]=num-a[i]
                arr.Add(num - array[i]);
            for (int i = 0; i < array.Count-1; i++)
            {
                if (array[i] == array[i + 1])
                    array.RemoveAt(i);
                if (arr[i] == arr[i + 1])
                    arr.RemoveAt(i);
            }
            for (int i = 0, j = arr.Count - 1; i < array.Count || j >= 0;) //将arr和array组合成新集合
            {
                if(i==array.Count)
                {
                    for (int k = j; k >= 0; k--)
                        list.Add(arr[k]);
                    break;
                }
                if(j==-1)
                {
                    for(int k=i;k<array.Count;k++)
                        list.Add(array[k]);
                    break;
                }
                if (array[i] > arr[j])
                    list.Add(arr[j--]);
                else
                    list.Add(array[i++]);
            }
            int count = 0;          //用于记录有多少对重复
            List<int> result = new List<int>();
            for(int i=0;i<list.Count-1;i++)  
            {
                if (list[i] == list[i + 1])
                {
                    count++;
                    result.Add(list[i]);
                }
            }
            if (count >= 2)
                return result;
            else
            {
                result.Clear();
                result.Add(-1);
                return result;
            }
        }
View Code

 

测试代码如下:

技术分享
        static void Main(string[] args)
        {
            List<int> array = new List<int> { 12, 3, 10, 1, 2, 4, 5, 8, 6, 7};
            Random rnd = new Random();
            int num =rnd.Next(25);
            foreach (int a in array)
                Console.Write("{0} ", a);
            Console.WriteLine();
            Console.WriteLine("num={0}", num);
            MergeSort<int>(array, 0, array.Count - 1);
            List<int> result = FindSum(array, num);
            if (result[0] == -1)
                Console.WriteLine("该数组中找不到!");
            else
            {
                Console.WriteLine("此数组中存在两数之和为{0},且存在于以下几个数中", num);
                foreach (int a in result)
                    Console.Write("{0} ", a);
            }
            Console.ReadKey();
        }
View Code

 

其中一次运行结果如下:技术分享

此方法的原理:b=x-a,如果数组S和S‘中均有a,b则显然会出现两个连续相等数据(并且此时a,b恰好为所需值)(注,由于此方法同时将b=x-a,b==a的情况也包含进去,从而会出现单数次重复数据---如果排除此情况必然为双数次重复)

----------------

思考题:待续

Chapter 2 算法基础

标签:

原文地址:http://www.cnblogs.com/Paul-chen/p/4249950.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!