标签:
-------------------注明----------------
以下内容来自于《算法导论》 lz新手,存在各种错误以及各种不合理的地方望大家指出
核心: 将一个数插入到已经排好序的有序数列中,从而得到一个新的、个数加1的有序数列
形象描述:有两堆牌,左手一堆已经排好序,右手一堆未排好,将右手中的牌一张一张取出来,放到左手这堆牌中(保证每次放进去都使左手牌仍有序)
实现代码 C#(以下按升序排序):
public void InsertSortUp(List<int> array) { int key, j; //key用来代表取出的值 for (int i = 1; i < array.Count; i++) { key = array[i]; //insert array[i] into the sorted array[1...j-1] j = i - 1; while (j >= 0 && array[j] > key) { array[j + 1] = array[j]; j--; } array[j + 1] = key; } }
Note: 以上易错在于while循环部分将大于key的后移一位。
采用的测试代码如下:(以int类型为示范)
static void Main(string[] args) { List<int> array = new List<int> { 1, 23, 51, 21, 31, 23, 12 }; InsertSort Sort = new InsertSort(); Console.WriteLine("the original array is:"); foreach (int a in array) Console.Write("{0} ", a); Console.WriteLine(); //Ascending Sort Sort.InsertSortUp(array); Console.WriteLine("the sorted array is:"); foreach (int a in array) Console.Write("{0} ", a); Console.WriteLine(); Console.ReadKey(); }
运行结果如下:
2.1-1
<31><41,59,26,41,58>→<31,41><59,26,41,58>→<31,41,59><26,41,58>→<26,31,41,59><41,58>
→<26,31,41,41,59><58>→<26,31,41,41,58,59>
2.1-2
将上述升序代码中的array[j]>key改为array[j]<key便可实现降序排序
2.1-3
线性查找问题:
//if num=array[i],return i, else return -1; public int LSearch(List<int> array,int num) { for(int i=0;i<array.Count;i++) { if (num == array[i]) return i; } return -1; }
2.1-4
二进制数相加问题:
核心:将二进制数存进数组,如0111,也按常规数组思路,从左到右代表数组的下标(即0存储在A[0]中),根据对应项相加,遇2进1的原则。
//A+B=C (均为二进制数以及A,B的长度相等,不足用0添) public int[] Add(int[] A,int[] B,int n) { int temp=0,sum=0; int[] C=new int[n+1]; for(int i=n-1;i>=0;i--) { sum = A[i] + B[i]+temp; //temp用于代表进位值 if (sum >= 2) //存在进位情况 { C[i + 1] = sum - 2; temp = 1; } else //不存在进位情况 { C[i+1] = sum; temp = 0; } } C[0] = temp; //C比A和B多一位(该位也可能为0) return C; }
Note:二进制存入数组是越高位处于数组下标越小处。此外,可进一步改善为对任意二进制均适用的加法。
判断算法优劣,根据算法所需要的资源----大部分情况下,我们采用运行时间
往往取运行时间的增长量级或增长率---即最高次作为衡量一个算法的"优劣"
2.2-1
2.2-2
选择算法(依次找出最小的”往前扔”):
public void Selectsort(List<int> array) { int min, pos; for (int i = 0; i < array.Count-1; i++) { min = array[i]; //min用于存储最小值 pos = i; //pos用于存储最小值的下标 for (int j = i+1; j < array.Count; j++) { if (array[j] < min) { min = array[j]; pos = j; } } array[pos] = array[i]; array[i] = min; } }
2.2-3
线性查找问题(要查找元素等可能地为数组中的任意元素):
平均需检查输入序列元素数:
最坏情况需检查输入序列元素数:n
平均情况与最坏情况均为 θ(n)
2.2-4
尽量减少循环次数
分治模式的三个步骤:
归并排序:
合并(将两个规模小的有序数组合并为大的有序数组)
//含有哨兵的情况,arr1[n1]和arr2[n2]均为哨兵 private void Merge(List<int> array,int begin,int mid,int end) { int n1 = mid - begin+1; int n2 = end - mid; //输入的end代表数组最后一项而非数组长度 int[] arr1 = new int[n1+1]; int[] arr2 = new int[n2+1]; for (int i = 0; i < n1; i++) //赋值 arr1[i] = array[begin+i]; for (int i = 0; i < n2; i++) arr2[i] = array[mid+i+1]; arr1[n1] = Int16.MaxValue; arr2[n2] = Int16.MaxValue; int m = 0, n = 0; for (int k = begin; k <=end; k++) //依次取出两数组中较小值,此处为核心 { if (arr1[m] < arr2[n]) { array[k] = arr1[m]; m++; } else { array[k] = arr2[n]; n++; } } }
分解+解决(将n分解为两个n/2并递归地排列两个子序列):
public void Mergesort(List<int> array,int begin,int end) { int mid; if(begin<end) //直到每个子数组均只有单个值时停止分解进行求解 { mid = (begin + end) / 2; Mergesort(array, begin, mid); Mergesort(array, mid+1, end); Merge(array, begin, mid, end); } }
2.3-1
略
2.3-2
不使用哨兵的合并程序:
//无哨兵情况,利用continue语句. private void Merge2(List<int> array, int begin, int mid, int end) { int n1 = mid - begin + 1; int n2 = end - mid; int[] arr1 = new int[n1]; int[] arr2 = new int[n2]; for (int i = 0; i < n1; i++) arr1[i] = array[begin + i]; for (int i = 0; i < n2; i++) arr2[i] = array[mid + 1 + i]; int m = 0, n = 0; for (int k = begin; k <= end; k++) { if (m == n1) //此判断语句来取代哨兵 { array[k] = arr2[n]; n++; continue; } if (n == n2) { array[k] = arr1[m]; m++; continue; } if (arr1[m] < arr2[n]) array[k] = arr1[m++]; else array[k] = arr2[n++]; } }
2.3-3
数学归纳法:
step1: 当n=2时,左边(T(2)=2)=右边(T(2)=2lg2=2)成立。
step2: 不妨假设时 T(n)=nlgn成立。
step3: 当时,,所以成立,得证!
2.3-4
插入排序的递归描述:
//note:此处的end代表的是数组的长度 public void InsertSort(List<int> array,int begin,int end) { if(begin<end) { end--; InsertSort(array, begin, end); int key=array[end],i; for(i=end-1;i>=begin;i--) { if (array[i] > key) array[i + 1] = array[i]; else break; } array[i+ 1] = key; } }
2.3-5
二分查找:
public int BinarySearch(List<int> array,int begin,int end,int key) { int mid=(begin+end)/2; while(mid!=begin&&mid!=end) { if (array[mid] == key) return mid; else if (array[mid] < key) begin = mid; else end = mid; mid = (begin + end) / 2; } return -1; //-1代表没有找到 }
最坏运行时间:假设 ,则最多只能进行次对分,所以最坏运行时间 θ(lgn)
2.3-6
不可以,由于每次要将比key大的数后移一位,所以不可避免的需要进行m次移动。
2.3-7
n个整数的集合S和另一个整数x,确定S中是否存在两个其和刚好为x的元素: 且要求运行时间为θ(nlgn)—采用Method1: 归并排序+二分查找
二分查找部分:
private int FindKey(List<int> array,int begin,int end,int key) { int mid=(begin+end)/2; while(mid!=begin&&mid!=end) { if (array[mid] == key) return mid; else if (array[mid] < key) begin = mid; else end = mid; mid = (begin + end) / 2; } return -1; }
归并+整体查找
public List<int> FindNum(List<int> array,int num) { MergeSort sort = new MergeSort(); //归并排序 sort.Mergesort(array, 0, array.Count - 1); int key; int[] result = new int[array.Count]; List<int> res=new List<int>(); for(int i=0;i<array.Count;i++) { key = num - array[i]; result[i]=FindKey(array,0,array.Count-1,key); } for(int i=0;i<result.Length/2;i++) //为了避免重复采取的措施 { if (result[i] != -1&&res.Contains(result[i])==false) res.Add(i); } return res; }
method2:此方法来源于一篇博客(当时做的笔记,未写引用,忘见谅)
step1:对集合S进行排序,可以采用归并排序算法
step2:对S中每一个元素a,将b=x-a构造一个新的集合S‘,并对S’进行排序
step3:去除S和S‘中重复的数据
step4:将S和S‘按照大小进行归并,组成新的集合T,若干T中有两队及以上两个连续相等数据,说明集合S中存在两个整数其和等于x。
归纳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; } }
此方法的原理:b=x-a,如果数组S和S‘中均有a,b则显然会出现两个连续相等数据(并且此时a,b恰好为所需值)(注,由于此方法同时将b=x-a,b==a的情况也包含进去,从而会出现单数次重复数据---如果排除此情况必然为双数次重复)
2-1
归并排序+插入排序
在归并排序中对小数组采用插入排序—-使用插入排序来排序长度为k的n/k个子表。
插入排序(同文章开头的插入排序)
归并排序中的合并(采用上述中无哨兵的合并)
具体的分解+解决:
public void MSort(List<int> array, int begin, int end,int k) { int mid = (begin + end) / 2; ; if ((end-begin)>k) //当两者的间距小于等于k时采用插入排序 { MSort(array, begin, mid,k); MSort(array, mid + 1, end,k); } else InsertSort(array, begin, end); Merge2(array, begin, mid, end); }
a. 证明:每个长度为k的子序列进行插入排序需要时间 从而n/k个长度为k的子表需要时间为
b. 从n个数到n/k个k长度的数需要划分次数为:,即共有层,而每层所需时间为n,所以合并这些子表所需时间为
d. k的选择,当输入数据较好时,插入排序耗时少k选择大,否则k选择小。
2-2
冒泡排序
public void Bubblesort(List<int> array) { int temp; for(int i=0;i<array.Count;i++) for(int j=array.Count-1;j>i;j--) { if(array[j]<array[j-1]) { temp = array[j]; array[j] = array[j - 1]; array[j - 1] = temp; } } }
2-3 略
2-4
逆序对
定义: A[1…n]是一个有n个不同数的数组,若i<j,且A[i]>A[j],则对偶(i , j)称为A的一个逆序对。
a. <2, 3, 8, 6, 1>的5个逆序对(2, 1), (3, 1), (8, 6), (8, 1), (6, 1)
b. 由集合{1, 2, …, n}中的元素构成的降序排序数组具有最多的逆序对,有对。
c. 成正比,插入时间越长,逆序对的数量越多(此处插入排序是指升序排序)
d. 求逆序对数量的算法:合并时即进行排序同时记录每个子例中逆序对个数。
//归并算法求每个子数组中逆序对个数并进行排序 private int Merge2(List<int> array, int begin, int mid, int end) { int n1 = mid - begin + 1; int n2 = end - mid; int num = 0; int[] arr1 = new int[n1]; int[] arr2 = new int[n2]; for (int i = 0; i < n1; i++) arr1[i] = array[begin + i]; for (int i = 0; i < n2; i++) arr2[i] = array[mid + 1 + i]; int m = 0, n = 0; for (int k = begin; k <= end; k++) { if (m == n1) { array[k] = arr2[n]; n++; continue; } if (n == n2) { array[k] = arr1[m]; m++; continue; } if (arr1[m] < arr2[n]) array[k] = arr1[m++]; else { array[k] = arr2[n++]; num = num+(n1-m); //记录逆序对个数 } } return num; } //获取逆序对总数量 public void MSort(List<int> array, int begin, int end) { int mid; if (begin < end) { mid = (begin + end) / 2; MSort(array, begin, mid); MSort(array, mid + 1, end); int num=Merge2(array, begin, mid, end); result += num; } } //返回结果 private static int result; public int Getresult() { return result; }
标签:
原文地址:http://www.cnblogs.com/Paul-chen/p/4331488.html