前言
如果你没有扎实的数据结构的功底,不知道链表与队列、哈希表与二叉查找树使用等。不妨可以考虑从排序跟查找算法入手。
字符串排序
我们来处理一个按字母表顺序排序字符串的实际问题。比如说名单表、创建索引和许多其他情况下都会用到字符串排序。用strcmp()函数确定两个字符串的顺序。一般的做法是读取字符串函数、排序字符串并打印出来。现在来实现读入字符串,并排序字符串:
#include <stdio.h>
#include <string.h>//strcmp
#define SIZE 81 //限制输入的字符串长度
#define LEN 20 //读入的最多行数
void StringSort(char *str[], int num);
void StringShow(char *str[], int num);
char *s_gets(char *str, int num);
int main(int argc, char const *argv[])
{
char *pstr[LEN]; //内含指针变量的数组
char input[LEN][SIZE]; //储存输入的数组
int ct = 0; //输入计数
printf("Input up to %d lines,and I will sort them.\n",LEN );
printf("To stop,press the Enter key at a line's start.\n");
while (ct < LEN && s_gets(input[ct],SIZE) != NULL && input[ct][0] != '\0')
{
pstr[ct] = input[ct]; //设置指针指向字符串
ct++;
}
StringSort(pstr, ct);
StringShow(pstr, ct);
return 0;
}
//字符串排序
void StringSort(char *str[], int num)
{
int i = 0,j = 0;
char *temp = NULL;
for (i = 0; i < num -1; i++)
{
for (j = i+1; j < num; j++)
{
if (strcmp(str[i],str[j]) > 0)
{
temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
}
}
//打印字符串
void StringShow(char *str[], int num)
{
int i = 0;
puts("Here is the sorted list:\n");
for (i = 0; i < num; i++)
puts(str[i]); //排序后的指针
}
char * s_gets(char * st, int n)
{
char * ret_val;
char * find;
ret_val = fgets(st, n, stdin);
if (ret_val != NULL)
{
find = strchr(st, '\n');
if (find != NULL)
*find = '\0';
else
while(getchar() != '\n')
continue;
}
return ret_val;
}
输出结果:
该程序的巧妙之处在于排序的是指向字符串指针,而不是字符串本身。pstr[ct] = input[ct]; 这意味着指针pstr[i]指向数组input[i]的首字符。每个input[i]都是一个内含81个元素的数组。
如无必要不用排序字符串,所以程序使用临时储存区(input)数组获取用户输入的字符串。如果用户通过键盘模拟EOF或输入一行空行,将退出下面的循环:
while (ct < LEN && s_gets(input[ct],SIZE) != NULL && input[ct][0] != '\0')
冒泡排序
我们最熟悉不过的就是冒泡排序,经常的在面试题中出现。冒泡排序重复地走访过要排序的数列,一次比较两个相邻的元素,如果第一个比第二个大,就交换他们两个。
#include <stdio.h>
void BubbleSort (int arr[], int len) ;
void BubbleShow(int arr[],int len);
int main(int argc, char const *argv[])
{
int arr[] = { 11, 21, 13, 2, 56, 12, 67, 78, 37, 9, 56, 6, 90, 43 };
int len = (int) sizeof(arr) / sizeof(*arr);
BubbleSort (arr, len);
BubbleShow(arr,len);
return 0;
}
void BubbleSort (int arr[], int len)
{
int i = 0;
int j = 0;
int temp = 0;
for ( i = 0; i < len - 1; i++)
{
// 每趟 i 循环将最大(小)值固定到最后一位
for ( j = 0; j < len - 1 - i; j++)
{
// 每趟 j 循环循环没有被固定到后方的数字
if (arr[j] > arr[j + 1])
{
// arr[j] < arr[j + 1] 代表从小到大排序
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
void BubbleShow(int arr[],int len)
{
int i = 0;
puts("Here is the sorted list:\n");
for ( i = 0; i < len; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
输出结果:
从程序来看,整个冒泡排序就是通过两个嵌套循环,外层循环将此轮最大(小)值固定到此轮尾部,内层循环比较相邻的两个元素并决定是否交换位置。
选择排序
选择排序首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。接下来看看代码是怎么实现的。
#include <stdio.h>
void SelectSort(int arr[], int len);
void swap(int *a, int *b);
void SelectShow(int arr[],int len);
int main(int argc, char const *argv[])
{
int arr[] = { 11, 21, 13, 2, 56, 12, 67, 78, 37, 9, 56, 6, 90, 43 };
int len = (int) sizeof(arr) / sizeof(*arr);
SelectSort (arr, len);
SelectShow(arr,len);
return 0;
}
void SelectSort(int arr[], int len)
{
int i = 0;
int j = 0;
int min = 0;
for (i = 0; i <len -1; i++)
{
min = i;
for (j = i+1; j < len; j++) //遍历未排序的元素
{
if (arr[j] < arr[min]) //找到最小值
min = j;//记录最小值
}
swap(&arr[min],&arr[i]);
}
}
void swap(int *a, int *b)
{
int temp = 0;
temp = *a;
*a = *b;
*b = temp;
}
void SelectShow(int arr[],int len)
{
int i = 0;
puts("Here is the sorted list:\n");
for ( i = 0; i < len; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
输出结果:
顺便一提。C库中有一个更高级的排序函数:qsort()。该函数使用一个指向函数的指针进行排序比较。
二分查找
二分查找,也称折半查找,在某些情况下相比于顺序查找(链表、队列),使用折半查找算法的效率更高。但是该算法的使用的前提是静态查找表中的数据必须是有序的。
在学习二叉查找树之前,必须要先了解一些名词,我们借助下面的图来了解:
介绍树的基本名词:
1.根节点:数字56节点没有父节点,我们把它称为根节点。
2.父节点:如数字19节点的父节点为数字56节点。
3.子节点:在图中,根节点56有两个孩子,分别是19,80,它们都是56的子节点.
4.叶子节点:5、13、37、75、92等节点,它们没有子节点,因此是叶子节点。
二叉查找树是一种树的特殊形式,它的每个节点最多两个孩子节点,分别为左孩子和右孩子。而二叉查找树在此基础上,还有一个特点,就是每个节点比它左子树的节点值都要大,而比右子树的节点值都要小。 下面通代码实现二分查找树:
#include <stdio.h>
int bin_search( int arr[], int len, int key ); //二分查找
int main(int argc,char **argv)
{
int arr[] = {5,13,19,21,37,56,64,75,80,88,92};
int num = 0, addr = 0;
int len = (int) sizeof(arr) / sizeof(*arr);
printf("The number information searched is(5,13,19,21,37,56,64,75,80,88,92)\n");
printf("Please enter the number to be searched: ");
scanf("%d", &num);
addr = bin_search(arr, len, num);
if( addr != -1)
{
printf("Find the number %d ,location : %d\n", num, addr);
}
else
{
printf("Can't find!\n");
}
return 0;
}
//二分查找
int bin_search( int arr[], int len, int key )
{
int left, right, mid;
left = 0;
right = len-1;
while( left <= right )
{
mid = (left+right)/2;
if( arr[mid] == key )//找到
{
return mid; // 查找成功
}
if( arr[mid] < key ) //从左子树查找
{
left = mid + 1; // 在后半序列中查找
}
if( arr[mid] > key ) //从右边子树查找
{
right = mid - 1; // 在前半序列中查找
}
}
return -1; // 查找失败,最后一层还没有找到
}
输出结果:
通过二分查找的平均查找长度,相比顺序查找来说,明显二分查找的效率要高。