约瑟夫环问题,这是一个很经典算法
问题描述:N个人围成一圈,从第一个人开始报数,报到m的人出圈,剩下的人继续从1开始报数,报到m的人出圈;如此往复,直到所有人出圈。(模拟此过程,输出出圈的人的序号)
当初还没有学过数据结构,链表这些东西都没有听过,所以回去直接用c语言数组硬上做出来了.
有一个数组存贮着每个人的编号, 当数到3的时候把下标置为-1,表示已经移出了数组,然后到数组结束的位置在从头计数
#include <stdio.h>
void fun(int n)
{
int a[n] ;
int i = 0 ;
for(i=0;i<n;i++){
a[i]=i+1;
printf("%d准备就绪\n",a[i]);
}
int m=0;//用来数123
int k=0;//k记录移动到的a数组下标,k总是有效的数组下标,
int leftNum=n;//记录a数组中大于0的个数,如果只剩一个说明游戏结束。
while(1){
m++;
printf("当前的m=%d 对应的元素 %d\n",m,a[k]);
if(m==3){
printf("叮咚! 去除此元素 %d\n",a[k]);
a[k]=-1;//把这个元素移除。
m=0;//m归零,重新开始下一轮计数
leftNum--;//有效人数减1
printf("当前数组中有效的元素个数是 %d个\n",leftNum);
}
k++;
//寻找下一个元素,先向后寻找
for(;k<n;k++){
if(a[k]>0){
printf("向后找到的下一个元素 %d\n",a[k]);
break;
}
}
// 如果向后寻找失败,那就得从头开始继续找
if(k==n){
for(i=0;i<n;i++){
if(a[i]>0){
k=i;
printf("向前循环找到的下一个元素 %d\n",a[k]);
break;
}
}
}
//判断能否结束循环
//j==1,说明可以结束了,k中的就是那个唯一剩下的元素了
if(leftNum==1){
printf("最终留下的数字是 %d\n",a[k]);
break;
}
}
}
int main(void) {
fun(10);
return 0;
}
上面是纯C语言搞出来的,但是oc中有可变数组,用可变数组就方便多了, 移出的操作方便了很多,计数的时候也是考虑到结尾处需要置为起始位置.
#import "ViewController.h"
@interface ViewController ()
// 用数组操作比c语言方便多了
@property (nonatomic, strong) NSMutableArray *array;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/// 假设此处的步长为4
NSInteger m = 4;
NSInteger index = 0;
while (self.array.count>1) {
index +=m-1;
if (index >= self.array.count) {
index = index%self.array.count;
}
NSLog(@"即将移除 %@",self.array[index]);
[self.array removeObjectAtIndex:index];
}
NSLog(@"最后一个元素 : %@",self.array.firstObject);
}
- (NSMutableArray *)array {
if (_array == nil) {
_array = [NSMutableArray array];
for (int i = 1; i<=10; i++) {
[_array addObject:@(i)];
}
}
return _array;
}
@end
还可以使用队列, 一开始建一个队列存贮所有的下标, 然后前m-1个出队加入到队尾,第m个出队后丢弃,不断循环,直到队列中一个元素都没有
#import "ViewController.h"
@interface ViewController ()
//模拟队列
@property (nonatomic, strong) NSMutableArray *array;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/// 假设此处的步长为4
NSInteger m = 4;
NSInteger index = 0;// 统计步长
while (self.array.count > 0) {
index ++ ;
// 如果是第m个,丢弃
if (index == m) {
NSLog(@"即将移除 %@",self.array.firstObject);
[self.array removeObjectAtIndex:0];
index = 0;
continue;
}
// 0到m-1个,模拟队列操作,从队头移除,加入到队尾
NSObject * obj = self.array.firstObject;
[self.array addObject:obj];
[self.array removeObjectAtIndex:0];
}
}
- (NSMutableArray *)array {
if (_array == nil) {
_array = [NSMutableArray array];
for (int i = 1; i<=10; i++) {
[_array addObject:@(i)];
}
}
return _array;
}
@end
最后放上最正统的循环链表, 把最后一个的next指向首节点,根据步长循环,直到只剩下一个元素
#import <UIKit/UIKit.h>
typedef struct Node{
int data;
struct Node * next;
} Node;
int main(int argc, char * argv[]) {
Node * first = malloc(sizeof(Node));
first->data = 1;
Node * preNode = first;
// 生成其他节点
for (int i= 2; i<=10; i++) {
Node * next = malloc(sizeof(Node));
next->data = i;
preNode->next = next;
preNode = next;
}
/// 把最后一个元素的next指向第一个,形成循环链表
preNode->next = first;
// 假定步长为4
NSInteger m = 4;
Node * temp = first;
// 链表中还剩下10个元素
NSInteger leftNum = 10;
while (leftNum>0) {
// 向前走m-1步,退出循环的时候,temp就是被移除节点的前一个节点,
for (int i = 1; i<m-1; i++) {
temp = temp->next;
}
Node * removeNode = temp->next;
NSLog(@"即将移除 %d",removeNode->data);
temp->next = removeNode->next;
free(removeNode);
leftNum--;
temp = temp->next;
}
}
c语言写算法,真心有点累,还是oc好点
@interface Node : NSObject
+ (Node *)nodeWithArray:(NSArray *)array;
@property (nonatomic, assign) int data;
@property (nonatomic, strong) Node *next;
@end
----------------
#import "Node.h"
@implementation Node
+ (Node *)nodeWithArray:(NSArray *)array {
if (array.count==0) {
return nil;
}
Node * first = [[Node alloc] init];
first.data = [array.firstObject intValue];
Node * preNode = first;
for (int i = 1; i<array.count; i++) {
Node * next = [[Node alloc] init];
next.data = [array[i] intValue];
preNode.next = next;
preNode = next;
}
/// 是个循环引用,外界需要破除这个循环引用
preNode.next = first;
return first;
}
- (NSString *)description {
return [NSString stringWithFormat:@"%d ",self.data];
}
- (void)dealloc {
NSLog(@"%d dealloc",self.data);
}
@end
---------------------
- (void)viewDidLoad {
[super viewDidLoad];
Node * first = [Node nodeWithArray:@[@(1),@(2),@(3),@(4),@(5),
@(6),@(7),@(8),@(9),@(10)]];
NSInteger m = 4;
Node * temp = first;
NSInteger leftNum = 10;
while (leftNum>0) {
for (int i = 1; i<m-1; i++) {
temp = temp.next;
}
Node * removeNode = temp.next;
NSLog(@"即将移除 %d",removeNode.data);
temp.next = removeNode.next;
removeNode = nil;
leftNum--;
// temp变成填补过来的节点,比如移除了4,下一轮循环的新起点就是5
temp = temp.next;
}
/// 破除循环引用
temp.next = nil;
temp = nil;
}
个人最喜欢第二种和队列的方式, 代码好理解很多, 循环链表这个会产生循环引用, 要小心.
其实还有一种通过数学方法分析得到结果的方式, 效率上会提升很多, 但是推导过程比较复杂, 这里就不写了.