程序员常用的几种算法(建议刚毕业或者在找实习的大学生看看)

    // 如果找到的最小元素不在当前位置,则交换它们  
    if (minIndex != i) 
    {  
        temp = arr[i];  
        arr[i] = arr[minIndex];  
        arr[minIndex] = temp;  
    }  
}  

}

int main()
{
int arr[] = {64, 25, 12, 22, 11};
int n = sizeof(arr) / sizeof(arr[0]);

// 打印原始数组  
printf("原始数组:\n");  
for (int i = 0; i < n; i++) 
{  
    printf("%d ", arr[i]);  
}  
printf("\n");  
  
// 调用选择排序函数  
selectionSort(arr, n);  
  
// 打印排序后的数组  
printf("排序后的数组:\n");  
for (int i = 0; i < n; i++) 
{  
    printf("%d ", arr[i]);  
}  
printf("\n");  
  
return 0;  

}


#### 快速排序


快排是一种递归思想的排序算法,先比较其他的排序算法,它需要更多内存空间,但快排的语句频度是最低的,理论上时间效率是最高的。


快速排序的基本思路是:在待排序序列中随便选取一个数据,作为所谓“支点”,然后所有其他的数据与之比较,以从小到大排序为例,那么比支点小的统统放在其左边,比支点大的统统放在其右边,全部比完之后,支点将位与两个序列的中间,这叫做一次划分(partition)。



![](https://img-blog.csdnimg.cn/img_convert/c120fe33337e64a00786de1faae5795e.png)


一次划分之后,序列内部也许是无序的,但是序列与支点三者之间,形成了一种基本的有序状态,接下去使用相同的思路,递归地对左右两边的子序列进行排序,直到子序列的长度小于等于1为止。


// 黄色:支点


// 绿色:比支点小的数据


// 紫色:比支点大的数据


// 红色:用来和支点比较大小的(现在比的位置)


// 橙色:已经比较好的数据


![](https://img-blog.csdnimg.cn/direct/8b9ce8aa71c84e5cb7cfa9542c2fa30d.gif)


                        注意:快速排序操作复杂,上述gif动图并没有完全将之展示出来


示例代码: 



#include <stdio.h>

// 快速排序的函数原型声明
void quickSort(int arr[], int left, int right);

// 交换数组中两个元素的位置
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}

// 快速排序函数
void quickSort(int arr[], int left, int right)
{
if (left < right)
{
// 分割数组,并返回pivot的位置
int pivotIndex = partition(arr, left, right);

    // 对pivot左边的子数组进行递归排序  
    quickSort(arr, left, pivotIndex - 1);  

    // 对pivot右边的子数组进行递归排序  
    quickSort(arr, pivotIndex + 1, right);  
}  

}

// 分割数组的函数
int partition(int arr[], int left, int right)
{
// 选择最右侧的元素作为基准值
int pivot = arr[right];
int i = left - 1; // 指向小于基准值的元素的最后一个位置

for (int j = left; j < right; j++) 
{  
    // 如果当前元素小于或等于基准值  
    if (arr[j] <= pivot) 
    {  
        // 将小于基准值的元素移动到左边  
        i++;  
        swap(&arr[i], &arr[j]);  
    }  
}  

// 将基准值放到正确的位置  
swap(&arr[i + 1], &arr[right]);  
return i + 1; // 返回基准值的索引位置  

}

// 主函数
int main()
{
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr) / sizeof(arr[0]);

printf("原始数组:\n");  
for (int i = 0; i < n; i++) 
{  
    printf("%d ", arr[i]);  
}  
printf("\n");  

// 对数组进行快速排序  
quickSort(arr, 0, n - 1);  

printf("排序后的数组:\n");  
for (int i = 0; i < n; i++) 
{  
    printf("%d ", arr[i]);  
}  
printf("\n");  

return 0;  

}


#### 希尔排序


希尔排序(Shell Sort)是插入排序的一种更高效的改进版本,也称为缩小增量排序。它通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已经排好了的(只剩少量的数据需要插入到已排好序的序列中),所以速度很快。


希尔排序的基本思路是:




1. **选择一个增量序列**:增量序列的选取对希尔排序的时间性能至关重要。通常,初始增量较大,随后增量逐渐减少,直至增量为1,即最后一次增量排序后序列基本有序。
2. **分组排序**:根据当前的增量值,将待排序的序列分割成若干长度为m的子序列(m为当前增量),然后对每个子序列进行直接插入排序。
3. **减小增量**:每次排序完成后,将增量按照一定的规则减小,通常是除以2或其他小于1的正数。
4. **重复分组排序**:使用新的增量值重复分组排序的步骤,直到增量减小到1。当增量为1时,整个序列已经基本有序,这时进行一次普通的插入排序,即可得到完全有序的序列。


希尔排序在开始时增量较大,这样它可以让元素移动更远,从而更快地使整个序列接近有序。随着增量的逐渐减小,排序的粒度也越来越细,直到增量为1时,完成最后的微调。


希尔排序的时间复杂度依赖于增量序列的选取,对于最优的增量序列,希尔排序的时间复杂度可以达到O(n^1.3)左右,比普通的插入排序O(n^2)要好很多。但是,由于希尔排序的增量序列选择是一个尚未解决的数学问题,所以希尔排序的实际性能可能会因增量序列的不同而有所差异。在实际应用中,常用的增量序列有Hibbard增量序列、Sedgewick增量序列等。


希尔排序是插入排序的一种优化,它通过将比较的全部元素分为几个区域来提升插入排序的性能,使得算法在数据量大的时候也能有较好的表现。


比如如下图所示,有无无序列:


84、83、88、87、61、50、70、60、80、99


第一遍,先取间隔为(Δ=5Δ=5),即依次对以下5组数据进行排序:


**84**、83、88、87、61、**50**、70、60、80、99  
 84、**83**、88、87、61、50、**70**、60、80、99  
 84、83、**88**、87、61、50、70、**60**、80、99  
 84、83、88、**87**、61、50、70、60、**80**、99  
 84、83、88、87、**61**、50、70、60、80、**99**


注意,当对84和50进行排序时,其他的元素就像不存在一样。因此,经过上述间隔为5的一遍排序后,数据如下:


**50**、83、88、87、61、**84**、70、60、80、99  
 50、**70**、88、87、61、84、**83**、60、80、99  
 50、70、**60**、87、61、84、83、**88**、80、99  
 50、70、60、**80**、61、84、83、88、**87**、99  
 50、70、60、80、**61**、84、83、88、87、**99**


最终的结果(50、70、60、80、61、84、83、88、87、99)是经过这一遍间隔Δ=5Δ=5的情况下达成的,接下去缩小间隔重复如上过程。例如让间距Δ=3Δ=3:


**50**、70、60、**80**、61、84、**83**、88、87、**99**  
 50、**70**、60、80、**61**、84、83、**88**、87、99  
 50、70、**60**、80、61、**84**、83、88、**87**、99  
 50、70、60、**80**、61、84、**83**、88、87、**99**


将上述粗体的每一组数据进行排序,得到:


**50**、70、60、**80**、61、84、**83**、88、87、**99**  
 50、**61**、60、80、**70**、84、83、**88**、87、99  
 50、61、**60**、80、70、**84**、83、88、**87**、99  
 50、61、60、**80**、70、84、**83**、88、87、**99**


最终的结果(50、61、60、80、70、84、83、88、87、99)更加接近完全有序的序列。接下去继续不断减小间隔,最终令Δ=1Δ=1,确保每一个元素都在恰当的位置。动图展示如下:


![](https://img-blog.csdnimg.cn/direct/db71671289b94b46a1627714012ca8a4.gif)


示例代码:



#include <stdio.h>

void shellSort(int arr[], int n)
{
int gap, i, j, temp;

// 初始增量设为数组长度的一半,之后每次减半  
for (gap = n / 2; gap > 0; gap /= 2) 
{  
    // 对每个子序列进行插入排序  
    for (i = gap; i < n; i++) 
    {  
        temp = arr[i];  
          
        // 从当前元素开始,比较前一个间隔为gap的元素  
        for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) 
        {  
            // 如果前一个元素比当前元素大,则交换它们  
            arr[j] = arr[j - gap];  
        }  
          
        // 找到合适的位置,插入当前元素  
        arr[j] = temp;  
    }  
}  

}

int main()
{
int arr[] = {84, 83, 88, 87, 61, 50, 70, 60, 80, 99};
int n = sizeof(arr) / sizeof(arr[0]);

printf("原始数组:\n");  
for (int i = 0; i < n; i++) 
{  
    printf("%d ", arr[i]);  
}  
printf("\n");  
  
// 调用希尔排序函数  
shellSort(arr, n);  
  
printf("排序后的数组:\n");  
for (int i = 0; i < n; i++) 
{  
    printf("%d ", arr[i]);  
}  
printf("\n");  
  
return 0;  

}


 好,排序算法讲到这,五个算法,应届生起码要记住冒泡和插入。因为排序算法在面试和笔试中出现频率真的高!


### 查找算法基本概念


在一堆数据中,找到我们想要的那个数据,就是查找,也称为搜索,很容易想到,查找算法的优劣,取决于两个因素:


* 数据本身存储的特点
* 查找算法本身的特点


比如,如果数据存储是无序的,那么当我们想要在其中找到某个节点时,一般就只能对它们逐个比对。如果数据存储是有序且存放在一片连续的内存中,那么我们可以考虑从中间开始找(二分法)。因此可以看到,在实际应用中如果需要优化数据的查找(搜索)性能,我们主要从以上两方面入手,当然,有时数据的存储特性是无法更改的,那么此时就只能靠优化算法本身去达到提供程序性能的目的了。


查找算法在面试中出现频率低,但你要是学会一两种查找算法写在简历上,不是很加分吗?


![](https://img-blog.csdnimg.cn/direct/baaf1d5c03e34b1b8c671bd280cc9986.png)


#### **顺序查找算法(Sequential Search Algorithm)**


顺序查找算法是一种基本的查找算法,它的基本思路是从列表的一端开始,逐个检查每个元素,直到找到所需的元素或检查完所有元素为止。以下是顺序查找算法的详细思路:


1. **确定查找范围**:首先,需要确定在哪个数据集合或列表中进行查找。这个列表可以是一个数组、链表或其他数据结构。
2. **初始化指针或索引**:在列表的开始位置(通常是第一个元素)设置一个指针或索引。这个指针或索引用于跟踪当前正在检查的元素。
3. **比较元素**:将指针或索引指向的元素与目标值进行比较。如果两者相等,那么查找成功,返回该元素的位置或值。
4. **移动指针或索引**:如果当前元素不等于目标值,则将指针或索引向前移动一位,指向下一个元素。
5. **重复比较和移动**:重复步骤3和4,直到找到目标元素或指针或索引已经移动到列表的末尾。
6. **判断查找结果**:如果遍历完整个列表仍未找到目标元素,那么查找失败,返回相应的结果(如特殊值或错误信息)。


顺序查找算法的时间复杂度是O(n),其中n是列表的长度。这是因为在最坏的情况下,可能需要检查列表中的每个元素。尽管顺序查找在某些情况下可能不是最高效的算法(特别是当数据量很大或数据有序时),但它实现简单,对于小规模数据或特定场景仍然是一个可行的选择。


需要注意的是,顺序查找算法对于无序列表是有效的,但如果列表已经排序,那么可以考虑使用更高效的查找算法,如二分查找。此外,对于某些数据结构(如哈希表),可以直接通过键值快速定位到元素,无需遍历整个列表。


![](https://img-blog.csdnimg.cn/direct/3a565c6ed6b147c0ab8c7e841a1d531a.gif)


示例代码:



#include <stdio.h>

// 定义顺序查找函数
int sequentialSearch(int arr[], int n, int target)
{
int i;

// 遍历数组  
for (i = 0; i < n; i++) 
{  
    // 如果找到目标值,返回其索引  
    if (arr[i] == target) 
    {  
        return i;  
    }  
}  
  
// 如果没有找到目标值,返回-1  
return -1;  

}

int main() {
// 定义数组
int arr[] = {10, 14, 19, 26, 27, 31, 33, 35, 42, 44};
int n = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
int target; // 目标值
int index; // 目标值在数组中的索引

// 获取用户输入的目标值  
printf("请输入要查找的目标值: ");  
scanf("%d", &target);  
  
// 调用顺序查找函数  
index = sequentialSearch(arr, n, target);  
  
// 输出查找结果  
if (index != -1) 
{  
    printf("目标值 %d 在数组中的索引为 %d\n", target, index);  
} 
else 
{  
    printf("目标值 %d 不在数组中\n", target);  
}  
  
return 0;  

}


####  二分查找算法(Binary Search Algorithm)


二分查找算法是一种在有序数组中查找特定元素的搜索算法。其核心思想是将数组分成两半,通过比较目标值与中间元素的值,决定在哪一半中继续查找,从而每次都将查找范围减半,直至找到目标值或确定目标值不存在。以下是二分查找算法的详细思路:


1. **初始化**:
	* 确定要查找的有序数组 `arr` 和目标值 `target`。
	* 设置两个指针 `left` 和 `right`,分别指向数组的起始位置和结束位置。
2. **循环查找**:
	* 当 `left` 小于等于 `right` 时,执行以下步骤:
		+ 计算中间位置 `mid`,可以是 `(left + right) / 2`(整数除法)或者 `(left + right) >> 1`(位运算,更高效)。
		+ 比较 `arr[mid]` 与 `target`:
			- 如果 `arr[mid]` 等于 `target`,则找到了目标值,返回 `mid`。
			- 如果 `arr[mid]` 大于 `target`,说明目标值可能在 `mid` 的左侧,因此将 `right` 更新为 `mid - 1`。
			- 如果 `arr[mid]` 小于 `target`,说明目标值可能在 `mid` 的右侧,因此将 `left` 更新为 `mid + 1`。
3. **查找结束**:
	* 如果循环结束仍未找到目标值,说明 `target` 不在数组中,返回一个表示未找到的标志值,通常是 `-1`。


二分查找算法的时间复杂度是 O(log n),其中 n 是数组的长度。这是因为每次查找都将数组的范围减半,因此最多需要 log2(n) 次比较就能找到目标值或确定其不存在。这使得二分查找算法在处理大规模有序数组时非常高效。


示例代码:



#include <stdio.h>

// 二分查找函数
int binarySearch(int arr[], int left, int right, int target)
{
// 当 left 大于 right 时,说明查找区间为空,目标值不存在
if (left > right)
{
return -1; // 返回-1表示未找到目标值
}

// 计算中间位置  
int mid = left + (right - left) / 2;  
  
// 检查中间位置的值是否等于目标值  
if (arr[mid] == target) 
{  
    return mid; // 如果等于,返回中间位置索引  
} 
else if (arr[mid] > target) 
{  
    // 如果中间位置的值大于目标值,则在左半部分继续查找  
    return binarySearch(arr, left, mid - 1, target);  
} 
else 
{  
    // 如果中间位置的值小于目标值,则在右半部分继续查找  
    return binarySearch(arr, mid + 1, right, target);  
}  

}

int main()
{
int arr[] = {2, 3, 4, 10, 40}; // 示例有序数组
int n = sizeof(arr) / sizeof(arr[0]); // 数组长度
int target = 10; // 要查找的目标值
int result; // 存储查找结果

// 调用二分查找函数  
result = binarySearch(arr, 0, n - 1, target);  
  
// 输出查找结果  
if (result == -1) 
{  
    printf("目标值 %d 不在数组中\n", target);  
} 
else 
{  
    printf("目标值 %d 在数组中的索引为 %d\n", target, result);  
}  
  
return 0;  

}


代码中,`binarySearch` 函数执行二分查找算法。它接受一个有序数组 `arr`、数组的起始索引 `left`、数组的结束索引 `right` 以及目标值 `target` 作为参数。函数首先检查查找区间是否为空(即 `left` 是否大于 `right`),如果是,则返回 `-1` 表示未找到目标值。然后,它计算中间位置 `mid`,并检查 `arr[mid]` 是否等于 `target`。如果等于,返回 `mid`;如果不等于,则根据 `arr[mid]` 与 `target` 的大小关系,在左半部分或右半部分继续递归查找。在 `main` 函数中,我们定义了一个有序数组 `arr` 和一个目标值 `target`,然后调用 `binarySearch` 函数进行查找,并输出结果。如果找到目标值,输出其在数组中的索引;否则,输出目标值不在数组中的消息。请注意,为了使用二分查找,数组 `arr` 必须是有序的。如果数组无序,二分查找将不会按预期工作。


#### 分块查找算法(Block Search)


分块查找算法是一种结合了顺序查找和二分查找的折中方案,适用于数据量大且部分有序的情况。该算法的基本思路是将数据集合分成若干块(或称为桶),每块内部的数据可以有序也可以无序,但块与块之间需要满足一定的排序规则。同时,建立一个索引表(或称为索引块),用于记录每块的最大值(或其他标识信息)以及块的起始位置。比如: 在一本字典中查找某个汉字的时候,一般是先找目录,在目录中找到对应拼音或笔画,然后直接翻到对应的页面再往下找,很显然这能大大提高查找的效率。



![](https://img-blog.csdnimg.cn/img_convert/e7ee1fa846a87703990c9327396b680d.png)


以下是分块查找算法的具体思路:


1. **数据分块与索引表建立**:
	* 首先,根据数据的特性(如大小范围、分布情况等)将数据集合划分为多个块。每个块的大小可以根据实际情况进行调整,但通常建议块的大小适中,以便于管理和查找。
	* 对每个块内的数据进行排序(如果未排序),以便在块内部进行快速查找。块与块之间不需要保持有序,但通常要求后一块中的元素不小于前一块中的最大元素,以确保块之间没有重叠。
	* 建立索引表,用于记录每个块的最大值(或其他关键信息)以及块的起始位置。索引表通常按照块的顺序存储,并且需要保持有序,以便进行后续的查找操作。
2. **查找过程**:
	* 当需要查找某个目标值时,首先在索引表中进行查找。由于索引表是有序的,可以采用二分查找算法或顺序查找算法来快速定位目标值可能所在的块。
	* 一旦确定了目标值所在的块,就在该块内部进行顺序查找。由于块内的数据已经排序,因此顺序查找的效率会相对较高。
	* 如果在块内部找到了目标值,则返回其位置;如果遍历完整个块仍未找到目标值,则说明目标值不在数据集合中。


分块查找算法的优点在于它结合了顺序查找和二分查找的特点。当数据量很大时,通过分块可以减少每次查找的范围,从而提高查找效率。同时,由于块内数据可以有序也可以无序,因此分块查找算法对数据集合的预处理要求相对较低。然而,分块查找算法的性能仍然受到块大小和索引表建立方式的影响,需要根据具体情况进行优化和调整。


总之,分块查找算法是一种适用于大规模数据集合的查找方法,通过合理划分数据块和建立索引表,可以在保持一定查找效率的同时降低预处理成本。


示例代码:



#include <stdio.h>
#include <stdlib.h>

// 定义块的大小
#define BLOCK_SIZE 4

// 假设我们有一个最大索引数组,用于记录每块的最大值
int maxIndex[3]; // 因为有10个元素,分成3块(最后一块可能不满)

// 假设我们有一个有序的数据数组
int data[] = {10, 14, 19, 26, 27, 31, 33, 35, 42, 44};

// 分块查找函数
int blockSearch(int target)
{
int n = sizeof(data) / sizeof(data[0]); // 数组长度
int blockNum = n / BLOCK_SIZE; // 块的数量
int blockIndex = 0; // 当前块的索引
int low, high, mid;

// 使用二分查找确定目标值可能存在的块  
low = 0;  
high = blockNum - 1;  
while (low <= high) 
{  
    mid = (low + high) / 2;  
    if (target <= maxIndex[mid]) 
    {  

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

img
img

arch(int target)
{
int n = sizeof(data) / sizeof(data[0]); // 数组长度
int blockNum = n / BLOCK_SIZE; // 块的数量
int blockIndex = 0; // 当前块的索引
int low, high, mid;

// 使用二分查找确定目标值可能存在的块  
low = 0;  
high = blockNum - 1;  
while (low <= high) 
{  
    mid = (low + high) / 2;  
    if (target <= maxIndex[mid]) 
    {  

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

[外链图片转存中…(img-WpmoJw1h-1726130964226)]
[外链图片转存中…(img-cD4hfhkz-1726130964226)]

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值