二分查找与插入
前言
这个问题是来自一次笔试经历,要求使用二分法做插入操作。对于二分查找的话,自己还是很熟悉的,但对于使用二分来实现插入操作确实是没接触过。不过,二分插入的思想与二分查找的思想是有共通点的,所以在这里简单地记录一下。
二分查找
//C语言二分查找
int binarySearch(int data[],int n,int key)
{
int low=0,high=n-1;
int middle;
while(low <= high)
{
middle = (low+high)/2;
if(data[middle] > key)
high = middle - 1;
else if(data[middle] < key)
low =middle + 1;
else
return middle+1;
}
return -1;
}
二分插入
思路:对于已排好序的数组的插入操作,大概分为3种情况。第一种是队头插入,即插入元素为第一个数据;第二种是队尾插入,即插入元素为最后一个数据;第三种是队中插入,即插入元素为数组中间的情况。如图:

对于一个N维数组,可插入的位置为0~N。使用二分插入,首先需要定位要插入的位置,这就需要二分查找的思想来实现,之后得到了要插入的位置后就可以使用移动数组的方式正式插入新的数据。
//C语言二分插入
int binaryInsert(int data[],int n,int key)
{
int low=0,high=n-1;
int middle;
while(low <= high)
{
middle = (low + high)/2;
if(data[middle] > key)
{
//插入位置在data[middle]之前
high= middle-1;
if(high < low)
{
return low;
}
else if(data[high] <= key)
{
return high+1;
}
}
else if(data[middle]<key)
{
//插入位置在data[middle]之后
low = middle +1;
if(high<low)
{
return low;
}
else if(data[low] >= key)
{
return low+1;
}
}
else
{
return middle+1;
}
}
return 0;
}
对比于二分查找,有两个个分支判断多了两个判断。
首先来看第一个判断,if(high<low) ,这个条件表示到达临界条件的溢出,即整个数组都小于或都大于要插入的数。
如对于数组1,2,3,4,5;
当要插入6的时候,二分插入的搜索情况如下:

这里返回low,由于数组是以0下标开始的,所以0,1,2,3,4;而这里的low就是5。
当要插入0的时候,二分插入的搜索情况如下:

整个算法出现了两次相同的判断if(high<low),是否考虑移出统一处理?因为这个条件就是while(low<=high)的终止条件,完全可以移到while循环后处理?这里不可以简单地认为是这样,因为一旦low或high溢出,接下来的data[low]或data[high]就数组溢出了,整个算法就会出错。
当检查high或low合法后,后续的else if(data[high]<=key)或 else if(data[low] >= key)是一次再判断。因为不和二分查找一样,二分查找的key相等是唯一return条件,而在二分插入中,是要找到插入位置,即插入位置的前一个<= key , 后一个>= key,所以return条件不唯一。
使用C#实现二分插入并进行单元测试
public class BinaryInsert
{
public static int GetInsertLoc(List<int> intList, int key)
{
int low = 0, high = intList.Count-1, middle;
while (low <= high)
{
middle = (low + high) / 2;
if (intList[middle] > key)
{
//插入元素在middle前面
high = middle - 1;
if (high < low)
{
return low;
}
else if (intList[high] <= key)
{
return high+1;
}
}
else if (intList[middle] < key)
{
//插入元素在middle后面
low = middle + 1;
if (high < low)
{
return low;
}
else if (intList[low] >= key)
{
return low+1;
}
}
else
{
return middle+1;
}
}
return 0;
}
}
[TestFixture]
public class BinarySearchTest
{
private List<int> _intList;
/// <summary>
/// Eg:0 2 4 6 8 ...
/// </summary>
/// <param name="count">生成的个数</param>
private void SetList(int count)
{
_intList = new List<int>();
for (int i = 0; i < count; i++)
{
_intList.Add(i*2);
}
}
[Test]
public void Empty_List()
{
//Arrange
SetList(0);
//Act
int insertLoc = BinaryInsert.GetInsertLoc(_intList, 1);
//Assert
Assert.AreEqual(insertLoc, 0);
}
[Test]
public void List_Has_One_Num()
{
//Arrange
//Eg:
//Loc: 0 1
//Data: 0
SetList(1);
//Act
int insertLoc1 = BinaryInsert.GetInsertLoc(_intList, -100);
int insertLoc2 = BinaryInsert.GetInsertLoc(_intList, 100);
int insertLoc3 = BinaryInsert.GetInsertLoc(_intList, 0);
//Assert
Assert.AreEqual(insertLoc1, 0);
Assert.AreEqual(insertLoc2, 1);
Assert.AreEqual(insertLoc3, 1);
}
[Test]
public void List_Has_Non_Repeat_Nums()
{
//Arrange
//Eg:
//Loc:0 1 2 3 4 5
//Data:0 2 4 6 8
SetList(5);
//Act
int insertLoc1 = BinaryInsert.GetInsertLoc(_intList, -100);
int insertLoc2 = BinaryInsert.GetInsertLoc(_intList, 3);
int insertLoc3 = BinaryInsert.GetInsertLoc(_intList, 100);
int insertLoc4 = BinaryInsert.GetInsertLoc(_intList, 0);
int insertLoc5 = BinaryInsert.GetInsertLoc(_intList, 6);
int insertLoc6 = BinaryInsert.GetInsertLoc(_intList, 8);
//Assert
Assert.AreEqual(insertLoc1, 0);
Assert.AreEqual(insertLoc2, 2);
Assert.AreEqual(insertLoc3, 5);
Assert.AreEqual(insertLoc4, 1);
Assert.AreEqual(insertLoc5, 4);
Assert.AreEqual(insertLoc6, 5);
}
[Test]
public void List_Has_Repeat_Nums()
{
//Arrange
_intList = new List<int>{ 2,4,4,4,6,8 };
//Act
int insertLoc1 = BinaryInsert.GetInsertLoc(_intList, 4);
//Assert
Assert.AreEqual(insertLoc1, 3);
}
}


浙公网安备 33010602011771号