約瑟夫環問題,多種方式處理

約瑟夫環問題,這是一個很經典算法

問題描述: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 j=0;//記錄a數組中大於0的個數,如果只剩一個說明遊戲結束。
    while(1){
        
        m++;
        printf("當前的m=%d   對應的元素  %d\n",m,a[k]);

        if(m==3){
            a[k]=-1;//把這個元素移除。
            m=0;//m歸零,重新開始下一輪計數
        }
              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=0;
        for(i=0;i<n;i++){
            if(a[i]>0){
                j++;
                
            }
        }
        printf("當前數組中有效的元素個數是 %d\n",j);
        //j==1,說明可以結束了,k中的就是那個唯一剩下的元素了
        if(j==1){
            printf("最終留下的數字是 %d\n",a[k]);
            break;
        }
    }
    
    
    
}


int main(void) {
    fun(10);
    return 0;
}

下面是log信息: 

1
2
3
4
5
6
7
8
9
10
當前的m=1   對應的元素  1
向後找到的下一個元素  2
當前數組中有效的元素個數是 10
當前的m=2   對應的元素  2
向後找到的下一個元素  3
當前數組中有效的元素個數是 10
當前的m=3   對應的元素  3
向後找到的下一個元素  4
當前數組中有效的元素個數是 9
當前的m=1   對應的元素  4
向後找到的下一個元素  5
當前數組中有效的元素個數是 9
當前的m=2   對應的元素  5
向後找到的下一個元素  6
當前數組中有效的元素個數是 9
當前的m=3   對應的元素  6
向後找到的下一個元素  7
當前數組中有效的元素個數是 8
當前的m=1   對應的元素  7
向後找到的下一個元素  8
當前數組中有效的元素個數是 8
當前的m=2   對應的元素  8
向後找到的下一個元素  9
當前數組中有效的元素個數是 8
當前的m=3   對應的元素  9
向後找到的下一個元素  10
當前數組中有效的元素個數是 7
當前的m=1   對應的元素  10
向前循環找到的下一個元素  1
當前數組中有效的元素個數是 7
當前的m=2   對應的元素  1
向後找到的下一個元素  2
當前數組中有效的元素個數是 7
當前的m=3   對應的元素  2
向後找到的下一個元素  4
當前數組中有效的元素個數是 6
當前的m=1   對應的元素  4
向後找到的下一個元素  5
當前數組中有效的元素個數是 6
當前的m=2   對應的元素  5
向後找到的下一個元素  7
當前數組中有效的元素個數是 6
當前的m=3   對應的元素  7
向後找到的下一個元素  8
當前數組中有效的元素個數是 5
當前的m=1   對應的元素  8
向後找到的下一個元素  10
當前數組中有效的元素個數是 5
當前的m=2   對應的元素  10
向前循環找到的下一個元素  1
當前數組中有效的元素個數是 5
當前的m=3   對應的元素  1
向後找到的下一個元素  4
當前數組中有效的元素個數是 4
當前的m=1   對應的元素  4
向後找到的下一個元素  5
當前數組中有效的元素個數是 4
當前的m=2   對應的元素  5
向後找到的下一個元素  8
當前數組中有效的元素個數是 4
當前的m=3   對應的元素  8
向後找到的下一個元素  10
當前數組中有效的元素個數是 3
當前的m=1   對應的元素  10
向前循環找到的下一個元素  4
當前數組中有效的元素個數是 3
當前的m=2   對應的元素  4
向後找到的下一個元素  5
當前數組中有效的元素個數是 3
當前的m=3   對應的元素  5
向後找到的下一個元素  10
當前數組中有效的元素個數是 2
當前的m=1   對應的元素  10
向前循環找到的下一個元素  4
當前數組中有效的元素個數是 2
當前的m=2   對應的元素  4
向後找到的下一個元素  10
當前數組中有效的元素個數是 2
當前的m=3   對應的元素  10
向前循環找到的下一個元素  4
當前數組中有效的元素個數是 1
最終留下的數字是 4

上面是純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;
   
}

個人最喜歡第二種和隊列的方式, 代碼好理解很多, 循環鏈表這個會產生循環引用, 要小心

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章