2014/11/6
算法导论,插入排序:
过程描述:待排序序列数分为两部分,一部分为已排好序序列,一部分未排好,初始时,已排好个数为1。
从未排好序序列第一个数开始循环,如果已排好序列中的数大于第一个数,将该数右移,直到
遇见小于等于第一个数的数值,那么在该位置插入为未排序序列中的第一个数,之后的从第二
个数开始,重复过程,直到遍历完所有的数。
其中已排好序系列称为循环不变式,只要证明循环不变式在三个条件下成立,那么该算法设计就
是正确的,这三个条件分别是初始化,保持和终止。
插入排序伪代码过程如下:
顺序排列
INSERT-SORT(A)
for j = 1 to A.length
key = a[j]
i = j -1
while(A[i] > key && i > -1)
A[i+1] = A[i]
i = i - 1
A[I+1] = key
算法分析:插入排序的思想基于增量法,将[0~j-1]排序后,将单个元素A[j]插入子数组的合适位置,产生
新的排序子序列,直到子序列长度和原序列一样。
2014/11/6
算法导论,归并排序:
算法分析:归并排序是基于分治的思想,将一组序列的排序分解成多个更小的序列排序,然后从最小的序列
(长度为1的序列)开始递归向上求解,求解过程是对左右分治的已排好序序列进行比较,填充上一级序列。
以下为含四个数的序列,排序流程图:
排序算法C语言实现如下:
有一个小技巧需要注意,在合并流程里面,在每一个待合并序列的末端加入一个无穷大的数,
这样免去了数组为空的判断逻辑。
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <time.h>
#define MAXSIZE 4
#define ENDLESS 0xFFFFFFF
/*合并流程*/
void Merge(int *a,int l,int m,int r);
/*递归流程*/
void MergeSort(int *a,int l,int r);
void PrintInfo(int *a,int n);
int main()
{
int *a = (int *)malloc(sizeof(int)*MAXSIZE);
srand(time(0));
for(int i=0;i<MAXSIZE;i++)
{
a[i] = rand()%100;
}
PrintInfo(a,MAXSIZE);
MergeSort(a,0,MAXSIZE-1);
PrintInfo(a,MAXSIZE);
return 0;
}
void MergeSort(int *a,int l,int r)
{
if(l < r)
{
int mid = (l + r)/2;
MergeSort(a,l,mid);
MergeSort(a,mid+1,r);
PrintInfo(a,MAXSIZE);
Merge(a,l,mid,r);
}
}
void Merge(int *a,int l,int m,int r)
{
int nl = m - l + 1;
int nr = r - m;
int *left = (int *)malloc(sizeof(int)*(nl+1));
int *right = (int *)malloc(sizeof(int)*(nr+1));
int i,j,k=l;
for(i=0;i<nl;i++)
{
left[i] = a[l+i];
}
for(j=0;j<nr;j++)
{
right[j] = a[m+j+1];
}
left[i] = ENDLESS;
right[j] = ENDLESS;
i=j=0;
PrintInfo(left,nl+1);
PrintInfo(right,nr+1);
for(k=l;k<=r;k++)
{
if(left[i] <= right[j])
{
a[k] = left[i];
i++;
}
else
{
a[k] = right[j];
j++;
}
}
}
void PrintInfo(int *a,int n)
{
printf("Array info:");
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
2014/11/7
算法导论,分治策略,最大子数组和问题
n个数据最大子数组问题等价于,2个n/2最大子数组问题,2个n/2子数组问题又可以继续递归,直到问题分解
为1个数据的最大子数组问题,总共分解了lgn次。此时,最大子数组为自身.但是向上回朔求解子数组时候可
能有三种情况:
1.子数组左右边界都在左边部分
2.子数组左右边界都在右边部分
3.子数组跨越左右子集
基于这种思想,可以设计递归函数,分解成三部分问题.FIindCrossSubArray()求解跨越左右子集
问题的函数,跨越左右子集的边界计算是线性非递归的过程.
#include <stdio.h>
#include <limits.h>
#include <malloc.h>
#define MAXSIZE 4
/*分治发求解最大子数组问题*/
int *FindCrossSubArray(int *a,int low,int mid,int high)
{
int leftSum = INT_MIN;
int left = 0;
int sum = 0;
for(int i=mid;i>=0;i--)
{
sum = sum + a[i];
if(sum > leftSum)
{
leftSum = sum;
left = i;
}
}
sum = 0;
int rightSum = INT_MIN;
int right = 0;
for(int i=mid+1;i<=high;i++)
{
sum = sum + a[i];
if(sum > rightSum)
{
rightSum = sum;
right = i;
}
}
int r[3] = {left,right,leftSum+rightSum};
return r;
}
int *FindMaxSubArray(int *a,int low,int high)
{
if(low == high)
{
int r[3] = {low,high,a[low]};
return r;
}
else
{
int mid = (low + high)/2;
int *left = FindMaxSubArray(a,low,mid);
int *right = FindMaxSubArray(a,mid+1,high);
int *cross = FindCrossSubArray(a,low,mid,high);
if(left[2] >= right[2] && left[2] >= cross[2])
{
return left;
}
else if(right[2] >= left[2] && right[2] >= cross[2])
{
return right;
}
else
{
return cross;
}
}
}
/*
非递归实现
*/
int *FindMaxSubArray1(int *a,int low,int high)
{
int maxSum = INT_MIN;
int sum = 0;
int start = 0;
int end = 0;
for(int i=0;i<=high;i++)
{
sum = sum + a[i];
if(sum > maxSum)
{
maxSum = sum;
end = i;
}
if(sum < 0)
{
sum = 0;
start = i;
}
}
int r[3] = {start,end,maxSum};
return r;
}
int main()
{
int *a = (int *)malloc(sizeof(int)*MAXSIZE);
int low = 0;
int high = MAXSIZE - 1;
srand(time(0));
printf("\nArray info : ");
for(int i=0;i<MAXSIZE;i++)
{
a[i] = -rand()%20 + 10;
printf("%d ",a[i]);
}
int *r = FindMaxSubArray1(a,low,high);
printf("\nMax sub array info : left.%d,right.%d,sum.%d\n",r[0],r[1],r[2]);
free(a);
return 0;
}
Q.如果数组A全为负数,算法将产生什么结果?
分析:当A全为负数时,从最底层l=r=0开始求解,因为求解cross的时候,
无论往左还是往右都是负数,所以第lgn-1层的最大子数组就是lgn层的左
右子数组中最大的那个负数。
递归到第0层,就是k<(n-1)/2和k>(n-1)/2+1中的最大的那个负数。
lgn层
{ F(a,0,0) => r{0,0,a[0] }
{ F(a,1,1) => r{1,1,a[1] }
{ F(a,2,2) => r{2,2a[2] }
{ F(a,n-1,n-1) => r{n-1,n-1,a[n-1] }
lgn-1层
{ F(a,0,1) => r{max{F(a,0,0),F(a,1,1),cross(a,0,1)}}
{ F(a,2,3) => r{max{F(a,2,2),F(a,3,3),cross(a,2,3)}}
{ F(a,n-4,n-3) => r{max{F(a,n-4,n-4),F(a,n-3,n-3),cross(a,n-4,n-3)}}
{ F(a,n-2,n-1) => r{max{F(a,n-2,n-2),F(a,n-1,n-1),cross(a,n-2,n-1)}}
.......
0层(根)
F(a,0,n-1) =>r{max{F(a,0,n/2-1/2),F(a,n/2-1/2+1,n-1),cross(a,0,n-1)}}
2014/11/9
第二部分,排序和统计量,堆排序
MAX-HEAPIFY(H,i)
l = LEFT(i)
r = RIGHT(i)
if i<=h.heap-size && H[i] < H[l]
largest = l
else largest = i
if i<=h.heap-size && H[largest] < H[r]
largest = r
if i != largest
MAX-HEAPIFY(A,largest)
BUILD-HEAP
A.heap-size = A.length
for i=n/2 down to 0
MAX-HEAPIFY(A,i)
HEAP-SORT
for i=A.heap-size-1 downto 1
temp = A[i]
A[i] = A[0]
A[0] = temp
A.heap-size = A.heap-size-1
MAX-HEAPIFY(A,0)
完整代码实现:
#include <stdio.h>
#include <malloc.h>
#define PARENT(i) (i>>1)-1
#define LEFT(i) (i<<1)+1
#define RIGHT(i) (i<<1)+2
#define MAXSIZE 7
struct heap
{
int heapSize;
int length;
int *heapArray;
};
void MaxHeapfy(struct heap *h,int i)
{
int l = LEFT(i);
int r = RIGHT(i);
int largest = 0;
if(l <= h->heapSize-1 &&
h->heapArray[i] < h->heapArray[l])
{
largest = l;
}
else
{
largest = i;
}
if(r <= h->heapSize-1 &&
h->heapArray[largest] < h->heapArray[r])
{
largest = r;
}
if(largest != i)
{
int temp = h->heapArray[i];
h->heapArray[i] = h->heapArray[largest];
h->heapArray[largest] = temp;
MaxHeapfy(h,largest);
}
}
void BuildMaxHeap(struct heap *h)
{
h->heapSize = h->length;
for(int i=h->length/2+1;i>=0;i--)
{
MaxHeapfy(h,i);
}
}
void HeapSort(struct heap *h)
{
for(int i=h->heapSize-1;i>=1;i--)
{
int temp = h->heapArray[i];
h->heapArray[i] = h->heapArray[0];
h->heapArray[0] = temp;
h->heapSize--;
MaxHeapfy(h,0);
}
}
int main()
{
int *a = (int *)malloc(sizeof(int)*MAXSIZE);
struct heap *h = (struct heap *)malloc(sizeof(struct heap));
h->heapArray = a;
h->length = MAXSIZE;
h->heapSize = 0;
srand(time(0));
for(int i=0;i<MAXSIZE;i++)
{
h->heapArray[i] = rand()%200;
}
print_r(h->heapArray,MAXSIZE);
printf("\n");
BuildMaxHeap(h);
HeapSort(h);
print_r(h->heapArray,MAXSIZE);
free(a);
free(h);
return 0;
}