Hard 找最小的k个数(Selection Rank) @CareerCup

本文介绍了三种方法来寻找一个数组中最小的k个数:1)通过排序,时间复杂度为O(nlogn);2)使用最大堆,达到O(nlogk)的时间效率;3)重点讨论了Selection Rank算法,特别是其partition方法的实现,该方法易错且关键。已另撰文详细阐述Selection Rank算法:http://blog.youkuaiyun.com/fightforyourdream/article/details/21373279。

1 排序 O(nlogn)

2 建最大堆,插入新元素的同时,不断删除最大元素O(nlogk)

3 Selection Rank 算法,注意partition方法,极其容易写错!我会另外写一篇专门讲这个算法!

更新:已经写了:http://blog.youkuaiyun.com/fightforyourdream/article/details/21373279


package Hard;

import java.util.Arrays;

import CtCILibrary.AssortedMethods;

/**
 * Describe an algorithm to find the smallest 1 million numbers in 1 billion numbers. Assume that the computer memory can hold all one billion numbers.

译文:

描述一个算法,在10亿个数中找到最小的1百万个数。假设内存可以一次性装入这10亿个数。
 *
 */
public class S18_6 {

	class Parts {
		public int left;
		public int right;

		public Parts(int l, int r) {
			left = l;
			right = r;
		}
	}

	// 直接排序后取前requiredCount个元素
	public static int rankB(int[] array, int requiredCount) {
		int[] cloned = array.clone();
		Arrays.sort(cloned);
		return cloned[requiredCount];
	}

	public static void swap(int[] array, int i, int j) {
		int t = array[i];
		array[i] = array[j];
		array[j] = t;
	}

	public static boolean validate(int[] array, int left, int right, int pivot,
			int endLeft) {
		for (int i = left; i <= endLeft; i++) {
			if (array[i] > pivot) {
				return false;
			}
		}
		for (int i = endLeft + 1; i <= right; i++) {
			if (array[i] <= pivot) {
				return false;
			}
		}
		return true;
	}

	public static boolean validateFull(int[] array) {
		for (int i = 0; i < array.length; i++) {
			for (int j = i; j < array.length; j++) {
				for (int k = i; k <= j; k++) {
					int[] cloned = array.clone();
					int pivot = array[k];
					int p = partition(cloned, i, j, pivot);
					if (!validate(cloned, i, j, pivot, p)) {
						AssortedMethods.printIntArray(cloned);
						String val = p >= 0 && p < cloned.length ? String
								.valueOf(array[i]) : "?";
						System.out.println("pivot: " + pivot + " | " + p
								+ " | " + val);
						return false;
					}
				}
			}
		}
		return true;
	}

	public static boolean isUnique(int[] array) {
		int[] cloned = array.clone();
		Arrays.sort(cloned);
		for (int i = 1; i < cloned.length; i++) {
			if (cloned[i] == cloned[i - 1]) {
				return false;
			}
		}
		return true;
	}

	public static int max(int[] array, int left, int right) {
		int max = Integer.MIN_VALUE;
		for (int i = left; i <= right; i++) {
			max = Math.max(array[i], max);
		}
		return max;
	}

	public static int randomInt(int n) {
		return (int) (Math.random() * n);
	}

	public static int randomIntInRange(int min, int max) {
		return randomInt(max + 1 - min) + min;
	}

	// 调整array使得pivot左边都是比pivot小的,pivot右边都是比pivot大的
	public static int partition(int[] array, int left, int right, int pivot) {
		while (true) {
			while (left <= right && array[left] <= pivot) {
				left++;
			}
			
			while (left <= right && array[right] > pivot) {
				right--;
			}
			
			if (left > right) {
				return left - 1;
			}
			swap(array, left, right);
		}
	}

	// 容易写错!! O(n)
	public static int rank(int[] array, int left, int right, int requiredCount) {
		int pivot = array[randomIntInRange(left, right)];		// 选择一个pivot
		int leftEnd = partition(array, left, right, pivot); 		// returns end of left partition
		int leftSize = leftEnd - left + 1;			// 左半边比pivot小的数
		if (leftSize == requiredCount + 1) {		// 满足条件
			return max(array, left, leftEnd);
		} else if (requiredCount < leftSize) {	// leftSize多了,在leftSize区间中partition
			return rank(array, left, leftEnd, requiredCount);
		} else {											// leftSize小了,在leftEnd右侧继续partition
			return rank(array, leftEnd + 1, right, requiredCount - leftSize);
		}
	}

	public static void main(String[] args) {
		int numberOfTests = 1000;
		int count = 0;
		while (count < numberOfTests) {
			// 建一个包含10个范围在[-1000,1000]的随机数的数组
			int[] array = AssortedMethods.randomArray(10, -1000, 1000);
			if (isUnique(array)) {
				int requiredCount = AssortedMethods.randomIntInRange(0, array.length - 1);		// 按照递增排序,选择前n个数
				int rank1 = rank(array.clone(), 0, array.length - 1, requiredCount);
				int rank2 = rankB(array.clone(), requiredCount);

				if (rank1 != rank2) {
					System.out.println("ERROR: " + rank1 + " " + rank2);
					AssortedMethods.printIntArray(array);
				}
				count++;
			}
		}
		System.out.println("Completed " + count + " runs.");
	}

}





<think>我们正在处理S32K144微控制器的HardFault中断回调函数修改请求。根据ARM Cortex-M架构,HardFault是一种异常,在发生严重错误时触发。在S32K144中,我们可以通过重定义HardFault_Handler函数来定制处理。 注意:在S32K144开发中,通常使用S32 Design Studio IDE,并基于S32 SDK(或之前的标准外设驱动)。这里我们以裸机编程(不使用SDK)为例,演示如何修改HardFault中断处理函数。 步骤: 1. 在启动文件中,HardFault_Handler是默认的异常处理函数,通常在一个无限循环中。但我们可以覆盖这个默认实现。 2. 在我们的代码中,定义一个同名的函数,链接器将使用我们的实现代替默认的。 但是,有时我们需要更灵活地设置回调(例如,允许运行时改变处理函数)。我们可以使用函数指针来实现。 示例方案: - 定义一个指向HardFault处理函数的函数指针。 - 默认情况下,将它指向一个默认处理函数(比如一个无限循环)。 - 用户可以在运行时修改这个指针,使其指向自定义的函数。 然而,由于HardFault是异常而不是普通中断,且发生在严重错误时,运行时修改可能不安全。因此,更常见的做法是在编译时重写HardFault_Handler。 下面提供两种方法: ### 方法1:直接覆盖HardFault_Handler函数(推荐) 在您的源文件中(例如main.c)中,重新实现HardFault_Handler: ```c __attribute__((naked)) void HardFault_Handler(void) { __asm volatile( " tst lr, #4 \n" // 检查EXC_RETURN的位2,判断使用的是MSP还是PSP " ite eq \n" " mrseq r0, msp \n" // 如果使用的是MSP,则R0=MSP " mrsne r0, psp \n" // 否则,R0=PSP " ldr r1, [r0, #24] \n" // 从栈中获取PC(程序计数器)的值 " b hard_fault_handler_c \n" // 跳转到C函数,R0指向栈帧,R1是出错的PC ); } void hard_fault_handler_c(uint32_t *stack_frame, uint32_t pc) { // 在这里添加你的处理代码,例如打印错误信息 // stack_frame指向栈帧,可以从中提取其他寄存器值 // pc是出错时的指令地址 // 示例:无限循环,或者通过串口输出错误信息 while (1) { // 死循环,或者执行复位操作 } } ``` 注意:上述汇编部分用于获取触发HardFault时的栈指针,并将栈指针和PC值传递给C函数。这样我们可以分析错误。 ### 方法2:使用函数指针(不常用,但提供灵活性) 在启动文件中,将HardFault_Handler定义为弱符号(weak),然后在用户代码中重新定义。 启动文件(如startup_S32K144.S)中通常有类似: ``` .weak HardFault_Handler .type HardFault_Handler, %function HardFault_Handler: b . // 无限循环 ``` 我们在C文件中覆盖它: ```c void HardFault_Handler(void) { // 自定义处理 } ``` 但是,如果我们想运行时改变处理函数,可以这样: 声明一个函数指针变量: ```c void (*hardfault_callback)(void) = NULL; ``` 然后,在HardFault_Handler中: ```c void HardFault_Handler(void) { if (hardfault_callback != NULL) { hardfault_callback(); } else { // 默认处理:死循环 while (1); } } ``` 这样,我们可以在其他地方设置回调函数: ```c void my_hardfault_handler(void) { // 处理HardFault } // 设置回调 hardfault_callback = my_hardfault_handler; ``` 但是请注意:HardFault发生在严重错误时,系统可能已经处于不稳定状态。因此,在回调函数中应避免复杂操作(尤其动态内存分配、函数调用过多等),最好只进行简单的错误记录或复位操作。 考虑到安全性,推荐使用方法1,并在处理函数中尽快复位或记录关键信息(如寄存器值)后复位。 下面是一个完整示例(使用方法1): ```c #include <stdint.h> // 声明C处理函数 void hard_fault_handler_c(uint32_t *stack_frame, uint32_t pc); __attribute__((naked)) void HardFault_Handler(void) { __asm volatile( " tst lr, #4 \n" " ite eq \n" " mrseq r0, msp \n" " mrsne r0, psp \n" " ldr r1, [r0, #24] \n" // PC在栈帧中的位置是第7个元素(0-15: R0-R3,R12,LR,PC,PSR),偏移24字节 " b hard_fault_handler_c \n" ); } void hard_fault_handler_c(uint32_t *stack_frame, uint32_t pc) { // 这里可以通过串口打印错误信息,例如: // printf("Hard fault at PC = 0x%08X\n", pc); // 但实际上在没有串口初始化的情况下,可能无法使用printf,所以可以简单地将pc保存到全局变量,然后复位。 // 示例:保存错误地址并复位 volatile uint32_t *hardfault_pc = (volatile uint32_t*)0xE0000000; // 假设一个地址(实际可用全局变量) *hardfault_pc = pc; // 触发软件复位 SCB->AIRCR = (0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk; while (1); // 等待复位 } int main(void) { // 正常初始化 // ... // 制造一个错误(测试用) //*(volatile uint32_t*)0x00000000 = 0; // 写入非法地址,触发HardFault while (1) { // 主循环 } } ``` 注意:上面的复位操作使用了ARM Cortex-M的系统控制块(SCB)的复位请求。需要包含头文件<core_cm4.h>(注意S32K144是Cortex-M4F内核)。在S32K144中,通常使用S32K144.h,它已经包含了内核寄存器的定义。 ### 相关问题 §§相关问题§§ 1. 如何在HardFault处理函数中获取出错时的寄存器状态(如R0-R3, LR等)? 2. 如何通过调试器定位S32K144的HardFault原因? 3. HardFault常见的原因有哪些?如何避免? 4. 在HardFault处理函数中调用函数(如串口输出)是否安全?需要注意什么? </think>### S32K144修改HardFault中断回调函数示例 在S32K144中修改HardFault中断回调函数,需要通过重写默认的HardFault_Handler实现。以下是两种实现方法: #### **方法1:直接覆盖默认处理函数** 在启动文件(`startup_S32K144.S`)HardFault_Handler的弱定义,然后创建同名函数覆盖: ```c // 在main.c中重写HardFault处理函数 __attribute__((naked)) void HardFault_Handler(void) { __asm volatile( " tst lr, #4 \n" // 检查EXC_RETURN的位2 " ite eq \n" " mrseq r0, msp \n" // 使用MSP " mrsne r0, psp \n" // 使用PSP " ldr r1, [r0, #24] \n" // 获取PC值 " b HardFault_Handler_C \n" // 跳转到C函数 ); } void HardFault_Handler_C(uint32_t *stack_frame) { volatile uint32_t fault_pc = stack_frame[6]; // 提取出错时的PC值 volatile uint32_t fault_lr = stack_frame[5]; // 提取LR值 // 故障处理代码(示例:通过LED指示) PTE->PDOR |= (1 << 0); // 点亮LED0表示故障 // 可选:保存故障信息到Flash/NVM // ... while(1); // 死循环(或触发系统复位) } ``` #### **方法2:使用函数指针动态注册** ```c // 定义函数指针类型 typedef void (*HardFault_Callback_t)(uint32_t*); // 全局回调指针 volatile HardFault_Callback_t hardfault_cb = NULL; // 注册回调函数 void Register_HardFault_Handler(HardFault_Callback_t callback) { hardfault_cb = callback; } // 默认HardFault处理 void Default_HardFault_Handler(uint32_t *stack_frame) { // 基础故障处理 PTE->PDOR |= (1 << 1); // 点亮LED1 while(1); } // 汇编入口 __attribute__((naked)) void HardFault_Handler(void) { __asm volatile( " tst lr, #4 \n" " ite eq \n" " mrseq r0, msp \n" " mrsne r0, psp \n" " push {lr} \n" // 保存LR " blx %[callback] \n" // 调用C回调 : : [callback] "r" (hardfault_cb ? hardfault_cb : Default_HardFault_Handler) ); } // 主程序中注册自定义处理 int main(void) { // 初始化硬件 // ... // 注册自定义回调 Register_HardFault_Handler(Custom_Fault_Handler); while(1) { // 主程序逻辑 } } ``` #### **关键点说明** 1. **栈帧分析** - MSP/PSP选择:通过`EXC_RETURN`的位2判断当前栈指针 - 栈帧结构:包含R0-R3, R12, LR, PC, xPSR等寄存器[^1] $$ \text{栈帧} = \begin{bmatrix} R0 \\ R1 \\ R2 \\ R3 \\ R12 \\ \text{LR} \\ \text{PC} \\ \text{xPSR} \end{bmatrix} $$ 2. **安全注意事项** - 避免递归故障:禁用中断`__disable_irq()`后再操作外设 - 最小化操作:仅执行关键操作(LED/复位),避免复杂函数调用 - 存储错误地址:保存PC/LR值用于后续分析 3. **调试技巧** - 使用`SCB->CFSR`(Configurable Fault Status Register)定位故障类型 - 通过`SCB->HFSR`(HardFault Status Register)获取故障状态 - 结合IDE的故障分析工具解读寄存器值[^2]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值